diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/DefaultEnableWhenEvaluator.java new file mode 100644 index 00000000000..bd20aa7d56e --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/DefaultEnableWhenEvaluator.java @@ -0,0 +1,60 @@ +package org.hl7.fhir.dstu3.validation; + +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent; + +import java.util.List; + +import org.hl7.fhir.dstu3.model.QuestionnaireResponse; +import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; + +public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { + + @Override + public boolean isQuestionEnabled(QuestionnaireItemComponent item, List resp) { + + boolean enabled = true; + + if(item.hasEnableWhen()) { + + enabled = false; + + for( 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; + } + +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/IEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/IEnableWhenEvaluator.java new file mode 100644 index 00000000000..202922727f4 --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/IEnableWhenEvaluator.java @@ -0,0 +1,13 @@ +package org.hl7.fhir.dstu3.validation; + +import java.util.List; + +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.dstu3.model.QuestionnaireResponse; +import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; + +public interface IEnableWhenEvaluator { + + public boolean isQuestionEnabled(QuestionnaireItemComponent item, List theResponseItems); + +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java index 36df4e23dfe..8fc9f6a0489 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java @@ -2539,6 +2539,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress); else rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId()); + } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/QuestionnaireResponseValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/QuestionnaireResponseValidator.java index ef430b4d3ef..152fab86d4e 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/QuestionnaireResponseValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/QuestionnaireResponseValidator.java @@ -55,6 +55,18 @@ 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; + + public boolean isSkipEnabledCheck() { + return skipEnabledCheck; + } + + public void setSkipEnabledCheck(boolean skipEnabledCheck) { + this.skipEnabledCheck = skipEnabledCheck; + } public QuestionnaireResponseValidator(IWorkerContext theWorkerCtx) { this.myWorkerCtx = theWorkerCtx; @@ -215,7 +227,7 @@ public class QuestionnaireResponseValidator extends BaseValidator { List responseItems = findResponsesByLinkId(theResponseItems, linkId); if (responseItems.isEmpty()) { - 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 { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java index 32e77320c39..160ba53327e 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java @@ -17,6 +17,7 @@ import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemOptionComponent; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType; import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; @@ -303,6 +304,137 @@ public class QuestionnaireResponseValidatorDstu3Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString("No response found for required item link0")); } + + @Test + public void testRequiredQuestionWithEnableWhenHasAnswerTrue() { + + Questionnaire q = new Questionnaire(); + q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); + + // create the questionnaire + QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); + item1.setLinkId("link1").setRequired(true); + q.addItem(item1); + QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent(); + item1.addEnableWhen(enable); + enable.setQuestion("link0"); + enable.setHasAnswer(true); + + + //q.getItemFirstRep().addEnableWhen(). + //q.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING); + + QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); + + 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 + public void testRequiredQuestionWithEnableWhenHidesQuestion() { + + Questionnaire q = new Questionnaire(); + q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING); + + // create the questionnaire + QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); + item1.setLinkId("link1").setRequired(true); + q.addItem(item1); + QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent(); + item1.addEnableWhen(enable); + enable.setQuestion("link0"); + enable.setHasAnswer(true); + + + //q.getItemFirstRep().addEnableWhen(). + //q.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING); + + QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + //qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); + + 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 + public void testRequiredQuestionWithEnableWhenHasAnswerTrueWithAnswer() { + + Questionnaire q = new Questionnaire(); + q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); + + // create the questionnaire + QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); + item1.setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING); + q.addItem(item1); + QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent(); + item1.addEnableWhen(enable); + enable.setQuestion("link0"); + enable.setHasAnswer(true); + + + //q.getItemFirstRep().addEnableWhen(). + //q.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING); + + QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); + qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("BAR")); + + 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 testRequiredQuestionWithEnableWhenHasAnswerFalse() { + + Questionnaire q = new Questionnaire(); + q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); + + // create the questionnaire + QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); + item1.setLinkId("link1").setRequired(true); + q.addItem(item1); + QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent(); + item1.addEnableWhen(enable); + enable.setQuestion("link0"); + enable.setHasAnswer(false); + + + //q.getItemFirstRep().addEnableWhen(). + //q.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING); + + QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); + + 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()); + // FIXME: should be no assert, fix the assert + assertThat(errors.toString(), containsString("No response found for required item link2")); + } @Test public void testEmbeddedItemInChoice() {