Added support for Quantity in the default enable when evaluator

This commit is contained in:
Eeva Turkka 2018-11-28 13:36:56 +02:00 committed by Matti Uusitalo
parent b29e8d987c
commit 076d18977d
3 changed files with 103 additions and 33 deletions

View File

@ -24,18 +24,15 @@ package org.hl7.fhir.convertors;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.ContactDetail; import org.hl7.fhir.dstu3.model.ContactDetail;
import org.hl7.fhir.dstu3.model.Contributor.ContributorType; import org.hl7.fhir.dstu3.model.Contributor.ContributorType;
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
import org.hl7.fhir.dstu3.model.ExpansionProfile.DesignationIncludeDesignationComponent; import org.hl7.fhir.dstu3.model.ExpansionProfile.DesignationIncludeDesignationComponent;
import org.hl7.fhir.dstu3.model.ExpansionProfile.SystemVersionProcessingMode; import org.hl7.fhir.dstu3.model.ExpansionProfile.SystemVersionProcessingMode;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.model.Expression.ExpressionLanguage; import org.hl7.fhir.r4.model.Expression.ExpressionLanguage;
import org.hl7.fhir.r4.model.Questionnaire.EnableWhenBehavior; import org.hl7.fhir.r4.model.Questionnaire.EnableWhenBehavior;
import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Contributor;
import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Type; import org.hl7.fhir.r4.model.Type;

View File

@ -8,6 +8,8 @@ import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Questionnaire.*; import org.hl7.fhir.r4.model.Questionnaire.*;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
/** /**
* Evaluates Questionnaire.item.enableWhen against a QuestionnaireResponse. * Evaluates Questionnaire.item.enableWhen against a QuestionnaireResponse.
* Ignores possible modifierExtensions and extensions. * Ignores possible modifierExtensions and extensions.
@ -53,7 +55,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
if (operator == QuestionnaireItemOperator.EXISTS){ if (operator == QuestionnaireItemOperator.EXISTS){
Type answer = enableCondition.getAnswer(); Type answer = enableCondition.getAnswer();
if (!(answer instanceof BooleanType)){ if (!(answer instanceof BooleanType)){
throw new RuntimeException("Exists-operator requires answerBoolean"); throw new UnprocessableEntityException("Exists-operator requires answerBoolean");
} }
return new EnableWhenResult(((BooleanType)answer).booleanValue() != answerItems.isEmpty(), return new EnableWhenResult(((BooleanType)answer).booleanValue() != answerItems.isEmpty(),
linkId, enableCondition, questionnaireResponse); linkId, enableCondition, questionnaireResponse);
@ -69,23 +71,44 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
try { try {
actualAnswer = answer.asType(); actualAnswer = answer.asType();
} catch (FHIRException e) { } catch (FHIRException e) {
throw new RuntimeException("Unexpected answer type", e); throw new UnprocessableEntityException("Unexpected answer type", e);
} }
if (!actualAnswer.getClass().equals(expectedAnswer.getClass())) { if (!actualAnswer.getClass().equals(expectedAnswer.getClass())) {
throw new RuntimeException("Expected answer and actual answer have incompatible types"); throw new UnprocessableEntityException("Expected answer and actual answer have incompatible types");
} }
if (expectedAnswer instanceof Coding) { if (expectedAnswer instanceof Coding) {
return compareCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer, questionnaireItemOperator); return compareCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer, questionnaireItemOperator);
} else if ((expectedAnswer instanceof PrimitiveType)) { } else if ((expectedAnswer instanceof PrimitiveType)) {
return comparePrimitiveAnswer((PrimitiveType<?>)actualAnswer, (PrimitiveType<?>)expectedAnswer, questionnaireItemOperator); return comparePrimitiveAnswer((PrimitiveType<?>)actualAnswer, (PrimitiveType<?>)expectedAnswer, questionnaireItemOperator);
} else if (expectedAnswer instanceof Quantity) {
return compareQuantityAnswer((Quantity)expectedAnswer, (Quantity)actualAnswer, questionnaireItemOperator);
} }
// TODO: Quantity, Attachment, reference? // TODO: Attachment, reference?
throw new RuntimeException("Unimplemented answer type: " + expectedAnswer.getClass()); throw new UnprocessableEntityException("Unimplemented answer type: " + expectedAnswer.getClass());
} }
private boolean compareQuantityAnswer(Quantity actualAnswer, Quantity expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
return compareComparable(actualAnswer.getValue(), expectedAnswer.getValue(), questionnaireItemOperator);
}
private boolean comparePrimitiveAnswer(PrimitiveType<?> actualAnswer, PrimitiveType<?> expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) { private boolean comparePrimitiveAnswer(PrimitiveType<?> actualAnswer, PrimitiveType<?> expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
if (actualAnswer.getValue() instanceof Comparable){ if (actualAnswer.getValue() instanceof Comparable){
return compareComparable((Comparable)actualAnswer.getValue(), (Comparable) expectedAnswer.getValue(), questionnaireItemOperator);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return actualAnswer.equalsShallow(expectedAnswer);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return !actualAnswer.equalsShallow(expectedAnswer);
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
}
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
int result = ((Comparable)actualAnswer.getValue()).compareTo(expectedAnswer.getValue()); private boolean compareComparable(Comparable actual, Comparable expected,
QuestionnaireItemOperator questionnaireItemOperator) {
int result = actual.compareTo(expected);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){ if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == 0; return result == 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){ } else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
@ -99,13 +122,9 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_THAN){ } else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_THAN){
return result > 0; return result > 0;
} }
throw new RuntimeException("Bad operator for PrimitiveType comparison");
} else if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){ throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
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) { private List<Element> findQuestionAnswers(Element questionnaireResponse, String question) {
@ -134,7 +153,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){ } else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result == false; return result == false;
} }
throw new RuntimeException("Bad operator for Coding comparison"); throw new UnprocessableEntityException("Bad operator for Coding comparison");
} }
private boolean compareCodes(Coding expectedCoding, Coding value) { private boolean compareCodes(Coding expectedCoding, Coding value) {

View File

@ -328,12 +328,12 @@ public class QuestionnaireResponseValidatorDstu3Test {
} }
@Test @Test
public void testRequiredQuestionWithEnableWhenHidesQuestion() { public void testRequiredQuestionQuantityWithEnableWhenHidesQuestionHasAnswerTrue() {
Questionnaire q = new Questionnaire(); Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.QUANTITY);
// create the questionnaire //link1 question is enabled when link0 has answer
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
item1.setLinkId("link1").setRequired(true); item1.setLinkId("link1").setRequired(true);
q.addItem(item1); q.addItem(item1);
@ -341,12 +341,11 @@ public class QuestionnaireResponseValidatorDstu3Test {
item1.addEnableWhen(enable); item1.addEnableWhen(enable);
enable.setQuestion("link0"); enable.setQuestion("link0");
enable.setHasAnswer(true); enable.setHasAnswer(true);
enable.setAnswer(new Quantity().setValue(1L));
QuestionnaireResponse qa = new QuestionnaireResponse(); QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
//qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
String reference = qa.getQuestionnaire().getReference(); String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
@ -356,6 +355,62 @@ public class QuestionnaireResponseValidatorDstu3Test {
assertThat(errors.toString(), containsString("No issues")); assertThat(errors.toString(), containsString("No issues"));
} }
@Test
public void testRequiredQuestionQuantityWithEnableWhenHidesQuestionValue() {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.QUANTITY);
//link1 question is enabled when link0 has answer
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
item1.setLinkId("link1").setRequired(true);
q.addItem(item1);
QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent();
item1.addEnableWhen(enable);
enable.setQuestion("link0");
enable.setAnswer(new Quantity().setValue(2L));
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
qa.addItem().setLinkId("link0").addAnswer().setValue(new Quantity().setValue(1L));
String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No issues"));
}
@Test
public void testRequiredQuestionQuantityWithEnableWhenEnablesQuestionValue() {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.QUANTITY);
//link1 question is enabled when link0 has answer
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
item1.setLinkId("link1").setRequired(true);
q.addItem(item1);
QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent();
item1.addEnableWhen(enable);
enable.setQuestion("link0");
enable.setAnswer(new Quantity().setValue(1L));
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
qa.addItem().setLinkId("link0").addAnswer().setValue(new Quantity().setValue(1L));
String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No response found for required item link1"));
}
@Test @Test
public void testRequiredQuestionWithEnableWhenHasAnswerTrueWithAnswer() { public void testRequiredQuestionWithEnableWhenHasAnswerTrueWithAnswer() {
@ -451,7 +506,6 @@ public class QuestionnaireResponseValidatorDstu3Test {
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qr); ValidationResult errors = myVal.validateWithResult(qr);
assertThat(errors.toString(), Matchers.not(containsString("No issues"))); assertThat(errors.toString(), Matchers.not(containsString("No issues")));
} }