From 0c0f887ddf2888f9dc8a5fe9743bc56c0dc32475 Mon Sep 17 00:00:00 2001 From: Eeva Turkka Date: Thu, 1 Nov 2018 15:58:05 +0200 Subject: [PATCH 01/41] Added unit tests - Case when hasAnswer is false doesn't work with InstanceValidator yet. --- .../DefaultEnableWhenEvaluator.java | 60 ++++++++ .../validation/IEnableWhenEvaluator.java | 13 ++ .../dstu3/validation/InstanceValidator.java | 1 + .../QuestionnaireResponseValidator.java | 14 +- ...estionnaireResponseValidatorDstu3Test.java | 132 ++++++++++++++++++ 5 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/DefaultEnableWhenEvaluator.java create mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/IEnableWhenEvaluator.java 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() { From a832b58691f3de7215e8788a0eeba4f95397b119 Mon Sep 17 00:00:00 2001 From: Eeva Turkka Date: Fri, 2 Nov 2018 09:17:35 +0200 Subject: [PATCH 02/41] Fixed test cases to make EnableWhen disable questions --- ...uestionnaireResponseValidatorDstu3Test.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) 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 160ba53327e..32e9e31a973 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 @@ -306,7 +306,7 @@ public class QuestionnaireResponseValidatorDstu3Test { } @Test - public void testRequiredQuestionWithEnableWhenHasAnswerTrue() { + public void testRequiredQuestionWithEnableWhenHdesQuestionHasAnswerTrue() { Questionnaire q = new Questionnaire(); q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); @@ -334,7 +334,7 @@ public class QuestionnaireResponseValidatorDstu3Test { ValidationResult errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response found for required item link1")); + assertThat(errors.toString(), containsString("No issues")); } @Test @@ -353,9 +353,6 @@ public class QuestionnaireResponseValidatorDstu3Test { 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"); @@ -366,7 +363,7 @@ public class QuestionnaireResponseValidatorDstu3Test { ValidationResult errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response found for required item link1")); + assertThat(errors.toString(), containsString("No issues")); } @Test @@ -404,10 +401,10 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test - public void testRequiredQuestionWithEnableWhenHasAnswerFalse() { + public void testRequiredQuestionWithEnableWheHidesRequiredQuestionnHasAnswerFalse() { Questionnaire q = new Questionnaire(); - q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); + q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING); // create the questionnaire QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); @@ -425,6 +422,8 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + + // link1 should be disabled, because the enableWhen enables it when link0 doesn't haven an answer qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); String reference = qa.getQuestionnaire().getReference(); @@ -432,8 +431,7 @@ public class QuestionnaireResponseValidatorDstu3Test { 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")); + assertThat(errors.toString(), containsString("No issues")); } @Test From 15610c94988d38f21c903da2b1bef5381f38845f Mon Sep 17 00:00:00 2001 From: Eeva Turkka Date: Fri, 2 Nov 2018 12:03:48 +0200 Subject: [PATCH 03/41] Fixed unit test asserts --- ...estionnaireResponseValidatorDstu3Test.java | 45 ++----------------- 1 file changed, 3 insertions(+), 42 deletions(-) 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 32e9e31a973..6503df0d6d0 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 @@ -308,42 +308,10 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testRequiredQuestionWithEnableWhenHdesQuestionHasAnswerTrue() { - 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 issues")); - } - - @Test - public void testRequiredQuestionWithEnableWhenHidesQuestion() { - Questionnaire q = new Questionnaire(); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING); - // create the questionnaire + //link1 question is enabled when link0 has answer QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); item1.setLinkId("link1").setRequired(true); q.addItem(item1); @@ -352,11 +320,9 @@ public class QuestionnaireResponseValidatorDstu3Test { enable.setQuestion("link0"); enable.setHasAnswer(true); - 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); @@ -366,6 +332,7 @@ public class QuestionnaireResponseValidatorDstu3Test { assertThat(errors.toString(), containsString("No issues")); } + @Test public void testRequiredQuestionWithEnableWhenHasAnswerTrueWithAnswer() { @@ -401,12 +368,10 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test - public void testRequiredQuestionWithEnableWheHidesRequiredQuestionnHasAnswerFalse() { + public void testRequiredQuestionWithEnableWhenHidesRequiredQuestionnHasAnswerFalse() { 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); @@ -415,10 +380,6 @@ public class QuestionnaireResponseValidatorDstu3Test { 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"); From 92936d6889e0858486121508c7028245cc598ba4 Mon Sep 17 00:00:00 2001 From: Eeva Turkka Date: Fri, 2 Nov 2018 15:54:34 +0200 Subject: [PATCH 04/41] Unit tests up to date and maybe we need to use r4 path --- .../QuestionnaireResponseValidator.java | 2 + .../DefaultEnableWhenEvaluator.java | 52 ++++++++++++++++++- .../validation/IEnableWhenEvaluator.java | 7 ++- .../fhir/r4/validation/InstanceValidator.java | 16 ++++-- ...estionnaireResponseValidatorDstu3Test.java | 4 -- 5 files changed, 72 insertions(+), 9 deletions(-) rename hapi-fhir-validation/src/main/java/org/hl7/fhir/{dstu3 => instance}/validation/DefaultEnableWhenEvaluator.java (56%) rename hapi-fhir-validation/src/main/java/org/hl7/fhir/{dstu3 => instance}/validation/IEnableWhenEvaluator.java (65%) 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 152fab86d4e..c92e55c0375 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 @@ -36,6 +36,8 @@ 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; 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/instance/validation/DefaultEnableWhenEvaluator.java similarity index 56% rename from hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/DefaultEnableWhenEvaluator.java rename to hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java index bd20aa7d56e..5e63f463c01 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java @@ -1,12 +1,14 @@ -package org.hl7.fhir.dstu3.validation; +package org.hl7.fhir.instance.validation; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent; +import java.util.ArrayList; import java.util.List; import org.hl7.fhir.dstu3.model.QuestionnaireResponse; import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; +import org.hl7.fhir.r4.elementmodel.Element; public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { @@ -54,6 +56,54 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { } + return enabled; + } + + @Override + public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent item, + Element element) { + boolean enabled = true; + + if(item.hasEnableWhen()) { + + enabled = false; + + for( org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent enable : item.getEnableWhen()) { + + if(enable.hasAnswer()) { + // check if referenced question has answer + + String itemId = enable.getQuestion(); + + List items = new ArrayList(); + element.getNamedChildren("item", items); + + for(Element respItem : items) { + + // TODO toteuta uudelleen + + + } + + } else { + // and if not + + String itemId = enable.getQuestion(); + + List items = new ArrayList(); + element.getNamedChildren("item", items); + + for(Element respItem : items) { + + + } + + } + } + + } + + 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/instance/validation/IEnableWhenEvaluator.java similarity index 65% rename from hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/IEnableWhenEvaluator.java rename to hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java index 202922727f4..10501484723 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/validation/IEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java @@ -1,13 +1,18 @@ -package org.hl7.fhir.dstu3.validation; +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; import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; +import org.hl7.fhir.r4.elementmodel.Element; public interface IEnableWhenEvaluator { public boolean isQuestionEnabled(QuestionnaireItemComponent item, List theResponseItems); + + public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent qItem, + Element element); + } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index a57decbc5cb..f6a60c1b5e0 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -10,6 +10,8 @@ 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; @@ -198,6 +200,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private IEvaluationContext externalHostServices; private boolean noExtensibleWarnings; private String serverBase; + + private IEnableWhenEvaluator myEnableWhenEvaluator = new DefaultEnableWhenEvaluator(); + /* * Keeps track of whether a particular profile has been checked or not yet @@ -2777,9 +2782,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List mapItem = map.get(qItem.getLinkId()); if (mapItem != null) 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()); - } + 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 errors, Element answer, NodeStack stack) { 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 6503df0d6d0..e5b67b9ce76 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 @@ -348,10 +348,6 @@ public class QuestionnaireResponseValidatorDstu3Test { 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"); From 0dcc4fa18918dfc83da7aee154d8b9f5411f436b Mon Sep 17 00:00:00 2001 From: Okko Kauhanen Date: Mon, 5 Nov 2018 12:01:25 +0200 Subject: [PATCH 05/41] First implementation of enableWhen evaluator --- .../DefaultEnableWhenEvaluator.java | 253 +++++++++++------- .../validation/IEnableWhenEvaluator.java | 11 +- 2 files changed, 164 insertions(+), 100 deletions(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java index 5e63f463c01..b4cf1912c44 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java @@ -1,110 +1,177 @@ package org.hl7.fhir.instance.validation; -import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; -import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent; +import java.util.*; +import java.util.stream.Collectors; -import java.util.ArrayList; -import java.util.List; - -import org.hl7.fhir.dstu3.model.QuestionnaireResponse; 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. + * + */ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { + public static final String LINKID_ELEMENT = "linkId"; + public static final String ITEM_ELEMENT = "item"; + public static final String ANSWER_ELEMENT = "answer"; - @Override - public boolean isQuestionEnabled(QuestionnaireItemComponent item, List resp) { + @Override + public boolean isQuestionEnabled(org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent item, + List 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 - boolean enabled = true; + enabled = true; + } + } + + } + } + } + return enabled; + } - 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 + + @Override + public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem, Element questionnaireResponse) { + if (!questionnaireItem.hasEnableWhen()) { + return true; + } + List evaluationResults = questionnaireItem.getEnableWhen() + .stream() + .map(enableCondition -> evaluateCondition(enableCondition, questionnaireResponse, + questionnaireItem.getLinkId())) + .collect(Collectors.toList()); + return checkConditionResults(evaluationResults, questionnaireItem); + } + + + public boolean checkConditionResults(List evaluationResults, + QuestionnaireItemComponent questionnaireItem) { + if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ANY){ + return evaluationResults.stream().anyMatch(EnableWhenResult::isEnabled); + } if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ALL){ + return evaluationResults.stream().allMatch(EnableWhenResult::isEnabled); + } + //TODO: Throw exception? enableBehavior is mandatory when there are multiple conditions + return true; + } - enabled = true; - } - } - - } - } - - } - - - return enabled; - } - @Override - public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent item, - Element element) { - boolean enabled = true; + public EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition, + Element questionnaireResponse, String linkId) { + //TODO: Fix EnableWhenResult stuff + List 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); - if(item.hasEnableWhen()) { - - enabled = false; - - for( org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent enable : item.getEnableWhen()) { - - if(enable.hasAnswer()) { - // check if referenced question has answer - - String itemId = enable.getQuestion(); - - List items = new ArrayList(); - element.getNamedChildren("item", items); - - for(Element respItem : items) { + } + return new EnableWhenResult(false, linkId, enableCondition, questionnaireResponse); + } - // TODO toteuta uudelleen + private boolean evaluateAnswer(Element answer, Type expectedAnswer) { + org.hl7.fhir.r4.model.Type actualAnswer; + try { + actualAnswer = answer.asType(); + } catch (FHIRException e) { + throw new RuntimeException("Unexpected answer type", e); + } + if (!actualAnswer.getClass().isAssignableFrom(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); + } + // TODO: Quantity, Attachment, reference? + throw new RuntimeException("Unimplemented answer type: " + expectedAnswer.getClass()); + } - - } - - } else { - // and if not - - String itemId = enable.getQuestion(); - - List items = new ArrayList(); - element.getNamedChildren("item", items); - - for(Element respItem : items) { + private List findQuestionAnswers(Element questionnaireResponse, String question) { + List matchingItems = questionnaireResponse.getChildren(ITEM_ELEMENT) + .stream() + .flatMap(i -> findSubItems(i).stream()) + .filter(i -> hasLinkId(i, question)) + .collect(Collectors.toList()); + return matchingItems + .stream() + .flatMap(e -> extractAnswer(e).stream()) + .collect(Collectors.toList()); + } + + private List extractAnswer(Element item) { + return item.getChildrenByName(ANSWER_ELEMENT) + .stream() + .flatMap(c -> c.getChildren().stream()) + .collect(Collectors.toList()); + } - - } - - } - } - - } - - - return enabled; - } + private boolean validateCodingAnswer(Coding expectedAnswer, Coding actualAnswer) { + return compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer); + } + private boolean compareCodes(Coding expectedCoding, Coding value) { + if (expectedCoding.hasCode() != value.hasCode()) { + return false; + } + if (expectedCoding.hasCode()) { + return expectedCoding.getCode().equals(value.getCode()); + } + return true; + } + + private boolean compareSystems(Coding expectedCoding, Coding value) { + if (expectedCoding.hasSystem() != value.hasSystem()) { + return false; + } + if (expectedCoding.hasSystem()) { + return expectedCoding.getSystem().equals(value.getSystem()); + } + return true; + } + private List findSubItems(Element item) { + List results = item.getChildren(LINKID_ELEMENT) + .stream() + .flatMap(i -> findSubItems(i).stream()) + .collect(Collectors.toList()); + results.add(item); + return results; + } + + private boolean hasLinkId(Element item, String linkId) { + Element linkIdChild = item.getNamedChild(LINKID_ELEMENT); + if (linkIdChild != null && linkIdChild.getValue().equals(linkId)){ + return true; + } + return false; + } + + + } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java index 10501484723..a81500dfb4b 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java @@ -3,16 +3,13 @@ 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; import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; import org.hl7.fhir.r4.elementmodel.Element; public interface IEnableWhenEvaluator { - - public boolean isQuestionEnabled(QuestionnaireItemComponent item, List theResponseItems); - - - public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent qItem, - Element element); + public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent questionnaireItem, + Element questionnaireResponse); + public boolean isQuestionEnabled(QuestionnaireItemComponent item, List theResponseItems); + } From 91bf3b61f6730d5f9022587bf1054365aaca84ab Mon Sep 17 00:00:00 2001 From: Okko Kauhanen Date: Mon, 5 Nov 2018 12:55:02 +0200 Subject: [PATCH 06/41] Missing file --- .../instance/validation/EnableWhenResult.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/EnableWhenResult.java diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/EnableWhenResult.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/EnableWhenResult.java new file mode 100644 index 00000000000..46246739596 --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/EnableWhenResult.java @@ -0,0 +1,48 @@ +package org.hl7.fhir.instance.validation; + + +import org.hl7.fhir.r4.elementmodel.Element; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent; + +public class EnableWhenResult { + private final boolean enabled; + private final QuestionnaireItemEnableWhenComponent enableWhenCondition; + private final Element answerItem; + 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, + Element answerItem) { + this.enabled = enabled; + this.linkId = linkId; + this.answerItem = answerItem; + this.enableWhenCondition = enableWhenCondition; + } + + public boolean isEnabled() { + return enabled; + } + + public String getLinkId() { + return linkId; + } + + public Element getAnswerItem() { + return answerItem; + } + + public QuestionnaireItemEnableWhenComponent getEnableWhenCondition() { + return enableWhenCondition; + } +} From 38a2b17a5e7ee9c862ed101010f27d2f93ad37d0 Mon Sep 17 00:00:00 2001 From: Okko Kauhanen Date: Mon, 5 Nov 2018 13:00:32 +0200 Subject: [PATCH 07/41] fix enablewhen result --- .../fhir/r4/validation/EnableWhenResult.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/EnableWhenResult.java diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/EnableWhenResult.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/EnableWhenResult.java new file mode 100644 index 00000000000..4f6d5d7f7db --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/EnableWhenResult.java @@ -0,0 +1,48 @@ +package org.hl7.fhir.r4.validation; + + +import org.hl7.fhir.r4.elementmodel.Element; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent; + +public class EnableWhenResult { + private final boolean enabled; + private final QuestionnaireItemEnableWhenComponent enableWhenCondition; + private final Element answerItem; + 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, + Element answerItem) { + this.enabled = enabled; + this.linkId = linkId; + this.answerItem = answerItem; + this.enableWhenCondition = enableWhenCondition; + } + + public boolean isEnabled() { + return enabled; + } + + public String getLinkId() { + return linkId; + } + + public Element getAnswerItem() { + return answerItem; + } + + public QuestionnaireItemEnableWhenComponent getEnableWhenCondition() { + return enableWhenCondition; + } +} From 4c4c8ba87aea87a8694f5c9d6d084adc05a5a8b0 Mon Sep 17 00:00:00 2001 From: Okko Kauhanen Date: Mon, 5 Nov 2018 17:03:50 +0200 Subject: [PATCH 08/41] Added operator evaluation for R4 enableWhen --- .../QuestionnaireResponseValidator.java | 5 +- .../instance/validation/EnableWhenResult.java | 2 +- .../validation/IEnableWhenEvaluator.java | 15 --- .../DefaultEnableWhenEvaluator.java | 115 +++++++++--------- .../r4/validation/IEnableWhenEvaluator.java | 10 ++ .../fhir/r4/validation/InstanceValidator.java | 15 +-- .../hapi/validation/EnableWhenResult.java | 47 +++++++ 7 files changed, 123 insertions(+), 86 deletions(-) delete mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java rename hapi-fhir-validation/src/main/java/org/hl7/fhir/{instance => r4}/validation/DefaultEnableWhenEvaluator.java (55%) create mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/IEnableWhenEvaluator.java create mode 100644 hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/EnableWhenResult.java 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 c92e55c0375..0dbc72b4d2b 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 @@ -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,7 +226,7 @@ public class QuestionnaireResponseValidator extends BaseValidator { List responseItems = findResponsesByLinkId(theResponseItems, linkId); if (responseItems.isEmpty()) { - if ((skipEnabledCheck || myEnableWhenEvaluator.isQuestionEnabled(nextQuestionnaireItem, theResponseItems)) && 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/main/java/org/hl7/fhir/instance/validation/EnableWhenResult.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/EnableWhenResult.java index 46246739596..84afebe2980 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/EnableWhenResult.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/EnableWhenResult.java @@ -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, diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java deleted file mode 100644 index a81500dfb4b..00000000000 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java +++ /dev/null @@ -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 theResponseItems); - - -} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java similarity index 55% rename from hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java rename to hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java index b4cf1912c44..6e840f0a9b1 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java @@ -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 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 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); - - } - return new EnableWhenResult(false, linkId, enableCondition, questionnaireResponse); + enableCondition.getQuestion()); + 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); + } + 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 findQuestionAnswers(Element questionnaireResponse, String question) { List 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) { diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/IEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/IEnableWhenEvaluator.java new file mode 100644 index 00000000000..be567c9d8d4 --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/IEnableWhenEvaluator.java @@ -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); + +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index f6a60c1b5e0..25c093e1f55 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -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; @@ -2780,16 +2778,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 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 errors, Element answer, NodeStack stack) { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/EnableWhenResult.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/EnableWhenResult.java new file mode 100644 index 00000000000..8568fbe271e --- /dev/null +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/EnableWhenResult.java @@ -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; + } +} From c099fcda0ada98f413903b0c456154d9c1f65e52 Mon Sep 17 00:00:00 2001 From: Okko Kauhanen Date: Mon, 5 Nov 2018 17:49:12 +0200 Subject: [PATCH 09/41] Fixed bug in finding QuestionnaireResponse child items --- .../org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java index 6e840f0a9b1..26d63b8a936 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java @@ -46,7 +46,6 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { protected EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition, Element questionnaireResponse, String linkId) { - //TODO: Fix EnableWhenResult stuff List answerItems = findQuestionAnswers(questionnaireResponse, enableCondition.getQuestion()); QuestionnaireItemOperator operator = enableCondition.getOperator(); @@ -157,7 +156,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { return true; } private List findSubItems(Element item) { - List results = item.getChildren(LINKID_ELEMENT) + List results = item.getChildren(ITEM_ELEMENT) .stream() .flatMap(i -> findSubItems(i).stream()) .collect(Collectors.toList()); From 139ec6503fc9889ec65a38456d69dc52f37d88a7 Mon Sep 17 00:00:00 2001 From: Okko Kauhanen Date: Mon, 5 Nov 2018 18:39:13 +0200 Subject: [PATCH 10/41] Remove unnecessary throw --- .../org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java index 26d63b8a936..b321c3a6d5e 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java @@ -98,7 +98,6 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { } 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){ From b14035165069554c67ddd31dc730e998fb80dc00 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Tue, 6 Nov 2018 13:11:12 +0200 Subject: [PATCH 11/41] Added default enablewhen behaviour to STU3 resources converted to R4 --- .../java/org/hl7/fhir/convertors/VersionConvertor_30_40.java | 2 ++ .../java/org/hl7/fhir/r4/validation/InstanceValidator.java | 2 +- .../validation/QuestionnaireResponseValidatorDstu3Test.java | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java index f5016c93e8b..fb040d87e1e 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java @@ -32,6 +32,7 @@ import org.hl7.fhir.dstu3.model.ExpansionProfile.DesignationIncludeDesignationCo import org.hl7.fhir.dstu3.model.ExpansionProfile.SystemVersionProcessingMode; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.model.Expression.ExpressionLanguage; +import org.hl7.fhir.r4.model.Questionnaire.EnableWhenBehavior; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Contributor; import org.hl7.fhir.r4.model.Identifier; @@ -16184,6 +16185,7 @@ public class VersionConvertor_30_40 { tgt.setType(convertQuestionnaireItemType(src.getType())); for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent t : src.getEnableWhen()) tgt.addEnableWhen(convertQuestionnaireItemEnableWhenComponent(t)); + tgt.setEnableBehavior(EnableWhenBehavior.ANY); if (src.hasRequired()) tgt.setRequired(src.getRequired()); if (src.hasRepeats()) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 25c093e1f55..2f23a7cecae 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2782,7 +2782,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress); } else { //item is missing, is the question enabled? - if (!myEnableWhenEvaluator.isQuestionEnabled(qItem, element)) { + 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()); } } 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 e5b67b9ce76..4053eeedd56 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 @@ -24,6 +24,7 @@ import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemC import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.util.ArrayList; @@ -52,10 +53,10 @@ public class QuestionnaireResponseValidatorDstu3Test { private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(); private static FhirContext ourCtx = FhirContext.forDstu3(); private FhirInstanceValidator myInstanceVal; - private FhirValidator myVal; + private static FhirValidator myVal; private IValidationSupport myValSupport; private IWorkerContext myWorkerCtx; - + @Before public void before() { myValSupport = mock(IValidationSupport.class); From 130fc3a90ed93ff691a22680bf2db4e20e5e3217 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Wed, 7 Nov 2018 11:46:24 +0200 Subject: [PATCH 12/41] Removed unused HapiWorkerContext --- .../QuestionnaireResponseValidatorDstu3Test.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) 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 4053eeedd56..04904db6077 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 @@ -7,8 +7,6 @@ import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; -import org.apache.commons.io.IOUtils; -import org.hamcrest.Matchers; import org.hl7.fhir.dstu3.context.IWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; @@ -24,20 +22,17 @@ import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemC import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; -import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -53,15 +48,12 @@ public class QuestionnaireResponseValidatorDstu3Test { private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(); private static FhirContext ourCtx = FhirContext.forDstu3(); private FhirInstanceValidator myInstanceVal; - private static FhirValidator myVal; + private FhirValidator myVal; private IValidationSupport myValSupport; - private IWorkerContext myWorkerCtx; @Before public void before() { myValSupport = mock(IValidationSupport.class); - // new DefaultProfileValidationSupport(); - myWorkerCtx = new HapiWorkerContext(ourCtx, myValSupport); myVal = ourCtx.newValidator(); myVal.setValidateAgainstStandardSchema(false); @@ -307,7 +299,7 @@ public class QuestionnaireResponseValidatorDstu3Test { } @Test - public void testRequiredQuestionWithEnableWhenHdesQuestionHasAnswerTrue() { + public void testRequiredQuestionWithEnableWhenHidesQuestionHasAnswerTrue() { Questionnaire q = new Questionnaire(); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING); From 84c02d2e795b807269704f5878efb125763e35e8 Mon Sep 17 00:00:00 2001 From: Eeva Turkka Date: Fri, 2 Nov 2018 09:17:35 +0200 Subject: [PATCH 13/41] Cherry picked changes from master to 3.5.0 branch --- hapi-fhir-converter/pom.xml | 3 +- hapi-fhir-validation/pom.xml | 27 +++++++------ ...estionnaireResponseValidatorDstu3Test.java | 40 +++++++++++++++++-- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index c2795fa0544..e817311729a 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -11,6 +11,7 @@ hapi-fhir-converter bundle + 3.5.1-PHRFIX-SNAPSHOT @@ -84,7 +85,7 @@ ca.uhn.hapi.fhir hapi-fhir-client - ${project.version} + 3.5.0 test diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 6723bc84d70..df441a53d21 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -11,6 +11,7 @@ hapi-fhir-validation bundle + 3.5.1-PHRFIX-SNAPSHOT HAPI FHIR - Validation @@ -18,17 +19,17 @@ ca.uhn.hapi.fhir hapi-fhir-base - ${project.version} + 3.5.0 ca.uhn.hapi.fhir hapi-fhir-utilities - ${project.version} + 3.5.0 ca.uhn.hapi.fhir hapi-fhir-converter - ${project.version} + 3.5.1-PHRFIX-SNAPSHOT @@ -63,49 +64,49 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - ${project.version} + 3.5.0 true ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - ${project.version} + 3.5.0 true ca.uhn.hapi.fhir hapi-fhir-structures-dstu2.1 - ${project.version} + 3.5.0 true ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - ${project.version} + 3.5.0 true ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - ${project.version} + 3.5.0 true ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - ${project.version} + 3.5.0 true ca.uhn.hapi.fhir hapi-fhir-structures-r4 - ${project.version} + 3.5.0 true ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - ${project.version} + 3.5.0 true @@ -118,13 +119,13 @@ ca.uhn.hapi.fhir hapi-fhir-server - ${project.version} + 3.5.0 test ca.uhn.hapi.fhir hapi-fhir-client - ${project.version} + 3.5.0 test 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 04904db6077..a23718d0f06 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 @@ -299,12 +299,12 @@ public class QuestionnaireResponseValidatorDstu3Test { } @Test - public void testRequiredQuestionWithEnableWhenHidesQuestionHasAnswerTrue() { + public void testRequiredQuestionWithEnableWhenHdesQuestionHasAnswerTrue() { Questionnaire q = new Questionnaire(); - q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING); + q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); - //link1 question is enabled when link0 has answer + // create the questionnaire QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); item1.setLinkId("link1").setRequired(true); q.addItem(item1); @@ -313,9 +313,11 @@ public class QuestionnaireResponseValidatorDstu3Test { enable.setQuestion("link0"); enable.setHasAnswer(true); + 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); @@ -325,7 +327,35 @@ public class QuestionnaireResponseValidatorDstu3Test { assertThat(errors.toString(), containsString("No issues")); } + @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); + + + 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 issues")); + } + @Test public void testRequiredQuestionWithEnableWhenHasAnswerTrueWithAnswer() { @@ -357,10 +387,12 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test - public void testRequiredQuestionWithEnableWhenHidesRequiredQuestionnHasAnswerFalse() { + public void testRequiredQuestionWithEnableWheHidesRequiredQuestionnHasAnswerFalse() { 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); From 69d9e5d2b2c3c1d7df047925fd3772f34df01f45 Mon Sep 17 00:00:00 2001 From: Eeva Turkka Date: Fri, 2 Nov 2018 15:54:34 +0200 Subject: [PATCH 14/41] Unit tests up to date and maybe we need to use r4 path --- hapi-fhir-converter/pom.xml | 4 +- hapi-fhir-validation/pom.xml | 28 ++--- .../QuestionnaireResponseValidator.java | 2 + .../DefaultEnableWhenEvaluator.java | 110 ++++++++++++++++++ .../validation/IEnableWhenEvaluator.java | 18 +++ .../fhir/r4/validation/InstanceValidator.java | 5 +- 6 files changed, 150 insertions(+), 17 deletions(-) create mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java create mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index e817311729a..37dc3dd0f2d 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -11,7 +11,7 @@ hapi-fhir-converter bundle - 3.5.1-PHRFIX-SNAPSHOT + 3.6.1-PHRFIX-SNAPSHOT @@ -85,7 +85,7 @@ ca.uhn.hapi.fhir hapi-fhir-client - 3.5.0 + 3.6.0 test diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index df441a53d21..8206fcd81bb 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -11,7 +11,7 @@ hapi-fhir-validation bundle - 3.5.1-PHRFIX-SNAPSHOT + 3.6.1-PHRFIX-SNAPSHOT HAPI FHIR - Validation @@ -19,17 +19,17 @@ ca.uhn.hapi.fhir hapi-fhir-base - 3.5.0 + 3.6.0 ca.uhn.hapi.fhir hapi-fhir-utilities - 3.5.0 + 3.6.0 ca.uhn.hapi.fhir hapi-fhir-converter - 3.5.1-PHRFIX-SNAPSHOT + 3.6.1-PHRFIX-SNAPSHOT @@ -64,49 +64,49 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 3.5.0 + 3.6.0 true ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 3.5.0 + 3.6.0 true ca.uhn.hapi.fhir hapi-fhir-structures-dstu2.1 - 3.5.0 + 3.6.0 true ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 3.5.0 + 3.6.0 true ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 3.5.0 + 3.6.0 true ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 3.5.0 + 3.6.0 true ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 3.5.0 + 3.6.0 true ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 3.5.0 + 3.6.0 true @@ -119,13 +119,13 @@ ca.uhn.hapi.fhir hapi-fhir-server - 3.5.0 + 3.6.0 test ca.uhn.hapi.fhir hapi-fhir-client - 3.5.0 + 3.6.0 test 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 0dbc72b4d2b..a409e2200c6 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 @@ -36,6 +36,8 @@ 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; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java new file mode 100644 index 00000000000..5e63f463c01 --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java @@ -0,0 +1,110 @@ +package org.hl7.fhir.instance.validation; + +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent; + +import java.util.ArrayList; +import java.util.List; + +import org.hl7.fhir.dstu3.model.QuestionnaireResponse; +import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; +import org.hl7.fhir.r4.elementmodel.Element; + +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; + } + + @Override + public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent item, + Element element) { + boolean enabled = true; + + if(item.hasEnableWhen()) { + + enabled = false; + + for( org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent enable : item.getEnableWhen()) { + + if(enable.hasAnswer()) { + // check if referenced question has answer + + String itemId = enable.getQuestion(); + + List items = new ArrayList(); + element.getNamedChildren("item", items); + + for(Element respItem : items) { + + // TODO toteuta uudelleen + + + } + + } else { + // and if not + + String itemId = enable.getQuestion(); + + List items = new ArrayList(); + element.getNamedChildren("item", items); + + for(Element respItem : items) { + + + } + + } + } + + } + + + return enabled; + } + +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java new file mode 100644 index 00000000000..10501484723 --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java @@ -0,0 +1,18 @@ +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; +import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; +import org.hl7.fhir.r4.elementmodel.Element; + +public interface IEnableWhenEvaluator { + + public boolean isQuestionEnabled(QuestionnaireItemComponent item, List theResponseItems); + + + public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent qItem, + Element element); + +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 2f23a7cecae..e8c25c1db25 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -5,11 +5,15 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.exceptions.*; import org.hl7.fhir.convertors.VersionConvertorConstants; 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; @@ -201,7 +205,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private IEnableWhenEvaluator myEnableWhenEvaluator = new DefaultEnableWhenEvaluator(); - /* * Keeps track of whether a particular profile has been checked or not yet */ From a12f5892e2e8b1bc498f29ee93b570644c78a43d Mon Sep 17 00:00:00 2001 From: Okko Kauhanen Date: Mon, 5 Nov 2018 17:03:50 +0200 Subject: [PATCH 15/41] Added operator evaluation for R4 enableWhen --- .../QuestionnaireResponseValidator.java | 16 +-- .../DefaultEnableWhenEvaluator.java | 110 ------------------ .../DefaultEnableWhenEvaluator.java | 7 +- .../fhir/r4/validation/InstanceValidator.java | 2 - 4 files changed, 5 insertions(+), 130 deletions(-) delete mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java 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 a409e2200c6..1209c78c8a4 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 @@ -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,17 +55,6 @@ public class QuestionnaireResponseValidator extends BaseValidator { */ private IWorkerContext myWorkerCtx; - - // 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; @@ -228,7 +215,7 @@ public class QuestionnaireResponseValidator extends BaseValidator { List responseItems = findResponsesByLinkId(theResponseItems, linkId); if (responseItems.isEmpty()) { - if ((skipEnabledCheck /*|| myEnableWhenEvaluator.isQuestionEnabled(nextQuestionnaireItem, theResponseItems)*/) && nextQuestionnaireItem.getRequired() ) { + if (nextQuestionnaireItem.getRequired()) { if (theValidateRequired) { rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required {0} with linkId[{1}]", itemType, linkId); } else { @@ -403,3 +390,4 @@ public class QuestionnaireResponseValidator extends BaseValidator { return allowedAnswerTypes; } } + diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java deleted file mode 100644 index 5e63f463c01..00000000000 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/DefaultEnableWhenEvaluator.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.hl7.fhir.instance.validation; - -import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; -import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent; - -import java.util.ArrayList; -import java.util.List; - -import org.hl7.fhir.dstu3.model.QuestionnaireResponse; -import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; -import org.hl7.fhir.r4.elementmodel.Element; - -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; - } - - @Override - public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent item, - Element element) { - boolean enabled = true; - - if(item.hasEnableWhen()) { - - enabled = false; - - for( org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent enable : item.getEnableWhen()) { - - if(enable.hasAnswer()) { - // check if referenced question has answer - - String itemId = enable.getQuestion(); - - List items = new ArrayList(); - element.getNamedChildren("item", items); - - for(Element respItem : items) { - - // TODO toteuta uudelleen - - - } - - } else { - // and if not - - String itemId = enable.getQuestion(); - - List items = new ArrayList(); - element.getNamedChildren("item", items); - - for(Element respItem : items) { - - - } - - } - } - - } - - - return enabled; - } - -} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java index b321c3a6d5e..78911bf2b92 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java @@ -46,6 +46,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { protected EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition, Element questionnaireResponse, String linkId) { + //TODO: Fix EnableWhenResult stuff List answerItems = findQuestionAnswers(questionnaireResponse, enableCondition.getQuestion()); QuestionnaireItemOperator operator = enableCondition.getOperator(); @@ -98,6 +99,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { } 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){ @@ -155,7 +157,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { return true; } private List findSubItems(Element item) { - List results = item.getChildren(ITEM_ELEMENT) + List results = item.getChildren(LINKID_ELEMENT) .stream() .flatMap(i -> findSubItems(i).stream()) .collect(Collectors.toList()); @@ -170,7 +172,4 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { } return false; } - - - } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index e8c25c1db25..2ddf143ebb1 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -12,8 +12,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; From d2c6a98c247bf65222dc46b098f669bbba732277 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Wed, 7 Nov 2018 11:46:24 +0200 Subject: [PATCH 16/41] Removed unused HapiWorkerContext --- .../validation/QuestionnaireResponseValidatorDstu3Test.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 a23718d0f06..f3eea7ae847 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 @@ -7,8 +7,6 @@ import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; -import org.hl7.fhir.dstu3.context.IWorkerContext; -import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult; import org.hl7.fhir.dstu3.model.*; @@ -299,7 +297,7 @@ public class QuestionnaireResponseValidatorDstu3Test { } @Test - public void testRequiredQuestionWithEnableWhenHdesQuestionHasAnswerTrue() { + public void testRequiredQuestionWithEnableWhenHidesQuestionHasAnswerTrue() { Questionnaire q = new Questionnaire(); q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); From 63d2af04dae164e75a9de713475f6bbd387e008e Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Thu, 8 Nov 2018 13:52:06 +0200 Subject: [PATCH 17/41] Added default operator type EQUALS --- .../org/hl7/fhir/convertors/VersionConvertor_30_40.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java index fb040d87e1e..63a24230ffc 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java @@ -16304,8 +16304,10 @@ public class VersionConvertor_30_40 { tgt.setOperator(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator.EXISTS); tgt.setAnswer(convertType(src.getHasAnswerElement())); } - else if (src.hasAnswer()) - tgt.setAnswer(convertType(src.getAnswer())); + else if (src.hasAnswer()) { + tgt.setOperator(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator.EQUAL); + tgt.setAnswer(convertType(src.getAnswer())); + } return tgt; } From 507c78320c4c62eb7f2b67472801e5cfc87a9dbe Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Fri, 9 Nov 2018 12:55:13 +0200 Subject: [PATCH 18/41] Added check for answering questions which are not enabled --- .../fhir/r4/validation/InstanceValidator.java | 1 + ...estionnaireResponseValidatorDstu3Test.java | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 2ddf143ebb1..4f0d335c27a 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2780,6 +2780,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (QuestionnaireItemComponent qItem : qItems) { List mapItem = map.get(qItem.getLinkId()); if (mapItem != null){ + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), myEnableWhenEvaluator.isQuestionEnabled(qItem, element), "Item has answer, even though it is not enabled "+qItem.getLinkId()); validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress); } else { //item is missing, is the question enabled? 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 f3eea7ae847..1fcbe07def7 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 @@ -7,6 +7,8 @@ import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; + +import org.hamcrest.Matchers; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult; import org.hl7.fhir.dstu3.model.*; @@ -413,6 +415,24 @@ public class QuestionnaireResponseValidatorDstu3Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString("No issues")); } + + @Test + public void testGivenQuestionIsNotEnabledWithEnableWhenAnswersAreReportedAsErrors() throws Exception { + Questionnaire q = new Questionnaire(); + q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING); + q.addItem().setLinkId("link2").setRequired(false).setType(QuestionnaireItemType.STRING).addEnableWhen().setQuestion("link0").setHasAnswer(true); + + QuestionnaireResponse qr = new QuestionnaireResponse(); + qr.setStatus(QuestionnaireResponseStatus.COMPLETED); + qr.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + + qr.addItem().setLinkId("link2").addAnswer().setValue(new StringType("FOO")); + + String reference = qr.getQuestionnaire().getReference(); + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); + ValidationResult errors = myVal.validateWithResult(qr); + assertThat(errors.toString(), Matchers.not(containsString("No issues"))); + } @Test public void testEmbeddedItemInChoice() { From e84689b16717850a6f7e8d6f7f26a30b6abb7b79 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Tue, 13 Nov 2018 08:27:10 +0200 Subject: [PATCH 19/41] Way to inject custom enablewhen evaluator added --- .../hapi/validation/FhirInstanceValidator.java | 14 ++++++++++++++ .../hl7/fhir/r4/validation/InstanceValidator.java | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index 83a227a6541..c09e9ea9115 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -31,6 +31,8 @@ import org.hl7.fhir.r4.utils.INarrativeGenerator; import org.hl7.fhir.r4.utils.IResourceValidator; import org.hl7.fhir.r4.utils.IResourceValidator.BestPracticeWarningLevel; import org.hl7.fhir.r4.utils.IResourceValidator.IdStatus; +import org.hl7.fhir.r4.validation.DefaultEnableWhenEvaluator; +import org.hl7.fhir.r4.validation.IEnableWhenEvaluator; import org.hl7.fhir.r4.validation.InstanceValidator; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -47,6 +49,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.function.Function; @SuppressWarnings({"PackageAccessibility", "Duplicates"}) public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule { @@ -60,6 +63,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid private IValidationSupport myValidationSupport; private boolean noTerminologyChecks = false; private volatile WorkerContextWrapper myWrappedWorkerContext; + private Function enableWhenEvaluatorSupplier = ctx -> new DefaultEnableWhenEvaluator(); /** * Constructor @@ -202,6 +206,15 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid public boolean isNoTerminologyChecks() { return noTerminologyChecks; } + + /** + * Sets a customized {@link IEnableWhenEvaluator} which is injected to created InstanceValidators + * @param myEnableWhenEvaluator + */ + public void setEnableWhenEvaluatorSupplier( + Function enableWhenEvaluatorSupplier) { + this.enableWhenEvaluatorSupplier = enableWhenEvaluatorSupplier; + } /** * If set to {@literal true} (default is false) the valueSet will not be validate @@ -235,6 +248,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid v.setAnyExtensionsAllowed(isAnyExtensionsAllowed()); v.setResourceIdRule(IdStatus.OPTIONAL); v.setNoTerminologyChecks(isNoTerminologyChecks()); + v.setMyEnableWhenEvaluator(enableWhenEvaluatorSupplier.apply(wrappedWorkerContext)); List messages = new ArrayList<>(); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 4f0d335c27a..fad7ef852bd 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2381,6 +2381,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat public void setAllowXsiLocation(boolean allowXsiLocation) { this.allowXsiLocation = allowXsiLocation; } + + public void setMyEnableWhenEvaluator(IEnableWhenEvaluator myEnableWhenEvaluator) { + this.myEnableWhenEvaluator = myEnableWhenEvaluator; + } /** * From 10fe878540cba4d2657d97b6ecd0841586bd5db3 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Wed, 14 Nov 2018 14:58:05 +0200 Subject: [PATCH 20/41] Changed versions to use fi.kela.kanta.phr parent --- hapi-fhir-converter/pom.xml | 1 + hapi-fhir-validation/pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 37dc3dd0f2d..88d7306044a 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -9,6 +9,7 @@ ../hapi-deployable-pom/pom.xml + fi.kela.kanta.phr hapi-fhir-converter bundle 3.6.1-PHRFIX-SNAPSHOT diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 8206fcd81bb..8833a2c3b95 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -27,7 +27,7 @@ 3.6.0 - ca.uhn.hapi.fhir + fi.kela.kanta.phr hapi-fhir-converter 3.6.1-PHRFIX-SNAPSHOT From 385df65509f7a4ce00cf0858c3b441b4a7433465 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Tue, 20 Nov 2018 14:04:56 +0200 Subject: [PATCH 21/41] Copying modifierExtensions from QuestionnaireItems added --- .../java/org/hl7/fhir/convertors/VersionConvertor_30_40.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java index 63a24230ffc..d1a05615d4a 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java @@ -30,6 +30,7 @@ import org.hl7.fhir.dstu3.model.Contributor.ContributorType; import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; import org.hl7.fhir.dstu3.model.ExpansionProfile.DesignationIncludeDesignationComponent; 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.r4.model.Expression.ExpressionLanguage; import org.hl7.fhir.r4.model.Questionnaire.EnableWhenBehavior; @@ -16202,6 +16203,9 @@ public class VersionConvertor_30_40 { tgt.addInitial().setValue(convertType(src.getInitial())); for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent t : src.getItem()) tgt.addItem(convertQuestionnaireItemComponent(t)); + for (org.hl7.fhir.dstu3.model.Extension t : src.getModifierExtension()) { + tgt.addModifierExtension(convertExtension(t)); + } return tgt; } From ab484c98190f7d030aa6adc1b331bae21f9eed5c Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Wed, 21 Nov 2018 15:35:54 +0200 Subject: [PATCH 22/41] Thread questionnaireResponse root element through questionnaire validation so implementations of enablewhen may validate against the complete resource --- .../fhir/r4/validation/InstanceValidator.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index fad7ef852bd..460fc105268 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2627,12 +2627,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat sdTime = sdTime + (System.nanoTime() - t); if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire could not be resolved, so no validation can be performed against the base questionnaire")) { boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); - validateQuestionannaireResponseItems(qsrc, qsrc.getItem(), errors, element, stack, inProgress); + validateQuestionannaireResponseItems(qsrc, qsrc.getItem(), errors, element, stack, inProgress, element); } } } - private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress) { + private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) { String text = element.getNamedChildValue("text"); rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId "+qItem.getLinkId()); @@ -2716,7 +2716,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // no validation break; } - validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, answer, stack, inProgress); + validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot); } if (qItem.getType() == null) { fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item "+qItem.getLinkId() + " does not contain a type"); @@ -2725,16 +2725,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat element.getNamedChildren("item", items); rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId()); } else { - validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, element, stack, inProgress); + validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot); } } - private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress) { + private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) { if (elements.size() > 1) rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); for (Element element : elements) { NodeStack ns = stack.push(element, -1, null, null); - validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress); + validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot); } } @@ -2746,7 +2746,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return -1; } - private void validateQuestionannaireResponseItems(Questionnaire qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress) { + private void validateQuestionannaireResponseItems(Questionnaire qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) { List items = new ArrayList(); element.getNamedChildren("item", items); // now, sort into stacks @@ -2761,7 +2761,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (qItem != null) { rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, "Structural Error: item is in the wrong place"); NodeStack ns = stack.push(item, -1, null, null); - validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress); + validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress, null); } else rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \""+linkId+"\" not found in questionnaire"); @@ -2784,11 +2784,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (QuestionnaireItemComponent qItem : qItems) { List mapItem = map.get(qItem.getLinkId()); if (mapItem != null){ - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), myEnableWhenEvaluator.isQuestionEnabled(qItem, element), "Item has answer, even though it is not enabled "+qItem.getLinkId()); - validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), myEnableWhenEvaluator.isQuestionEnabled(qItem, questionnaireResponseRoot), "Item has answer, even though it is not enabled "+qItem.getLinkId()); + validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, null); } else { //item is missing, is the question enabled? - if (myEnableWhenEvaluator.isQuestionEnabled(qItem, element)) { + if (myEnableWhenEvaluator.isQuestionEnabled(qItem, questionnaireResponseRoot)) { rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId()); } } From a9c625365d98ea816860e8fb704fa4eb1e13c944 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Tue, 27 Nov 2018 10:38:03 +0200 Subject: [PATCH 23/41] Added missing propagation of QuestionnaireResponse root element --- .../main/java/org/hl7/fhir/r4/validation/InstanceValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 460fc105268..3eb2a573a7a 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2785,7 +2785,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List mapItem = map.get(qItem.getLinkId()); if (mapItem != null){ rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), myEnableWhenEvaluator.isQuestionEnabled(qItem, questionnaireResponseRoot), "Item has answer, even though it is not enabled "+qItem.getLinkId()); - validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, null); + validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot); } else { //item is missing, is the question enabled? if (myEnableWhenEvaluator.isQuestionEnabled(qItem, questionnaireResponseRoot)) { From b29e8d987c0899d3e261fb1db5a04d81ef0cf9bd Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Tue, 27 Nov 2018 16:46:51 +0200 Subject: [PATCH 24/41] Avoid infinite loop when validating misaligned questionnaireresponseitems --- .../fhir/r4/validation/InstanceValidator.java | 2 +- ...estionnaireResponseValidatorDstu3Test.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 3eb2a573a7a..fc398e51d6a 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2761,7 +2761,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (qItem != null) { rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, "Structural Error: item is in the wrong place"); NodeStack ns = stack.push(item, -1, null, null); - validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress, null); + validateQuestionannaireResponseItem(qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot); } else rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \""+linkId+"\" not found in questionnaire"); 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 1fcbe07def7..34a6d9b76a3 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 @@ -430,7 +430,28 @@ public class QuestionnaireResponseValidatorDstu3Test { String reference = qr.getQuestionnaire().getReference(); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); + ValidationResult errors = myVal.validateWithResult(qr); + + assertThat(errors.toString(), Matchers.not(containsString("No issues"))); + } + + @Test + public void testGivenQuestionnaireResponseHasSiblingItemsWhenTheyShouldBeChildItems() throws Exception { + Questionnaire q = new Questionnaire(); + QuestionnaireItemComponent item = q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.GROUP); + item.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING); + + QuestionnaireResponse qr = new QuestionnaireResponse(); + qr.setStatus(QuestionnaireResponseStatus.COMPLETED); + qr.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qr.addItem().setLinkId("link0").setText("Text"); + qr.addItem().setLinkId("link1").addAnswer().setValue(new StringType("Answer")); + String reference = qr.getQuestionnaire().getReference(); + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); + + ValidationResult errors = myVal.validateWithResult(qr); + assertThat(errors.toString(), Matchers.not(containsString("No issues"))); } From 076d18977dce327e9d9065d07ce143a2a18747f4 Mon Sep 17 00:00:00 2001 From: Eeva Turkka Date: Wed, 28 Nov 2018 13:36:56 +0200 Subject: [PATCH 25/41] Added support for Quantity in the default enable when evaluator --- .../convertors/VersionConvertor_30_40.java | 3 - .../DefaultEnableWhenEvaluator.java | 67 ++++++++++++------- ...estionnaireResponseValidatorDstu3Test.java | 66 ++++++++++++++++-- 3 files changed, 103 insertions(+), 33 deletions(-) diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java index d1a05615d4a..9d0d1a38a4c 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java @@ -24,18 +24,15 @@ package org.hl7.fhir.convertors; import java.util.ArrayList; import java.util.List; -import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.ContactDetail; import org.hl7.fhir.dstu3.model.Contributor.ContributorType; import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; import org.hl7.fhir.dstu3.model.ExpansionProfile.DesignationIncludeDesignationComponent; 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.r4.model.Expression.ExpressionLanguage; import org.hl7.fhir.r4.model.Questionnaire.EnableWhenBehavior; 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.Reference; import org.hl7.fhir.r4.model.Type; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java index 78911bf2b92..8f296eedb84 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java @@ -8,6 +8,8 @@ import org.hl7.fhir.r4.elementmodel.Element; import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.Questionnaire.*; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; + /** * Evaluates Questionnaire.item.enableWhen against a QuestionnaireResponse. * Ignores possible modifierExtensions and extensions. @@ -53,7 +55,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { if (operator == QuestionnaireItemOperator.EXISTS){ Type answer = enableCondition.getAnswer(); 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(), linkId, enableCondition, questionnaireResponse); @@ -69,45 +71,62 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { try { actualAnswer = answer.asType(); } catch (FHIRException e) { - throw new RuntimeException("Unexpected answer type", e); + throw new UnprocessableEntityException("Unexpected answer type", e); } 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) { return compareCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer, questionnaireItemOperator); } else if ((expectedAnswer instanceof PrimitiveType)) { return comparePrimitiveAnswer((PrimitiveType)actualAnswer, (PrimitiveType)expectedAnswer, questionnaireItemOperator); + } else if (expectedAnswer instanceof Quantity) { + return compareQuantityAnswer((Quantity)expectedAnswer, (Quantity)actualAnswer, questionnaireItemOperator); } - // TODO: Quantity, Attachment, reference? - throw new RuntimeException("Unimplemented answer type: " + expectedAnswer.getClass()); + // TODO: Attachment, reference? + throw new UnprocessableEntityException("Unimplemented answer type: " + expectedAnswer.getClass()); } - private boolean comparePrimitiveAnswer(PrimitiveType actualAnswer, PrimitiveType expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) { + + + 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) { 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"); + 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 RuntimeException("Bad operator for PrimitiveType comparison"); + throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison"); } + @SuppressWarnings({ "rawtypes", "unchecked" }) + private boolean compareComparable(Comparable actual, Comparable expected, + QuestionnaireItemOperator questionnaireItemOperator) { + int result = actual.compareTo(expected); + + 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 UnprocessableEntityException("Bad operator for PrimitiveType comparison"); + + } + private List findQuestionAnswers(Element questionnaireResponse, String question) { List matchingItems = questionnaireResponse.getChildren(ITEM_ELEMENT) .stream() @@ -134,7 +153,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { } else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){ 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) { 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 34a6d9b76a3..13f79bdca7d 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 @@ -328,12 +328,12 @@ public class QuestionnaireResponseValidatorDstu3Test { } @Test - public void testRequiredQuestionWithEnableWhenHidesQuestion() { + public void testRequiredQuestionQuantityWithEnableWhenHidesQuestionHasAnswerTrue() { 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(); item1.setLinkId("link1").setRequired(true); q.addItem(item1); @@ -341,12 +341,11 @@ public class QuestionnaireResponseValidatorDstu3Test { item1.addEnableWhen(enable); enable.setQuestion("link0"); enable.setHasAnswer(true); + 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 StringType("FOO")); String reference = qa.getQuestionnaire().getReference(); 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")); } + @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 public void testRequiredQuestionWithEnableWhenHasAnswerTrueWithAnswer() { @@ -451,7 +506,6 @@ public class QuestionnaireResponseValidatorDstu3Test { when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); ValidationResult errors = myVal.validateWithResult(qr); - assertThat(errors.toString(), Matchers.not(containsString("No issues"))); } From 311d92f2765f301c71bcdbf78bafa50e3da3b9ee Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Wed, 28 Nov 2018 15:20:00 +0200 Subject: [PATCH 26/41] Added more information to the error message about misplaced QuestionnaireResponseItem --- .../fhir/r4/validation/InstanceValidator.java | 19 ++++++++++++------- ...estionnaireResponseValidatorDstu3Test.java | 3 +++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index fc398e51d6a..6e2a2c84846 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2745,7 +2745,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } return -1; } - + private void validateQuestionannaireResponseItems(Questionnaire qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) { List items = new ArrayList(); element.getNamedChildren("item", items); @@ -2759,7 +2759,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (index == -1) { QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); if (qItem != null) { - rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, "Structural Error: item is in the wrong place"); + rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem)); NodeStack ns = stack.push(item, -1, null, null); validateQuestionannaireResponseItem(qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot); } @@ -2770,11 +2770,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat { rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); lastIndex = index; - List mapItem = map.get(linkId); - if (mapItem == null) { - mapItem = new ArrayList(); - map.put(linkId, mapItem); - } + + List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>()); + mapItem.add(item); } } @@ -2795,6 +2793,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } +private String misplacedItemError(QuestionnaireItemComponent qItem) { + return qItem.hasLinkId() ? + String.format("Structural Error: item with linkid %s is in the wrong place", qItem.getLinkId()) + : + "Structural Error: item is in the wrong place"; +} + private void validateQuestionnaireResponseItemQuantity( List errors, Element answer, NodeStack stack) { } 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 13f79bdca7d..463bec25368 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 @@ -507,6 +507,9 @@ public class QuestionnaireResponseValidatorDstu3Test { ValidationResult errors = myVal.validateWithResult(qr); assertThat(errors.toString(), Matchers.not(containsString("No issues"))); + assertTrue("Must contain structural error about misplaced link1 item", + errors.getMessages().stream().filter(vm -> vm.getMessage().contains("Structural Error")) + .anyMatch(vm -> vm.getMessage().contains("link1"))); } @Test From 24ebe6cf03b9dd97c06e3b80d1eca434cf5e9839 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Thu, 29 Nov 2018 16:32:23 +0200 Subject: [PATCH 27/41] Temporarily set isNoTerminologyServer() return true to avoid errors about loinc codes --- .../hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index c09e9ea9115..e440315a803 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -637,7 +637,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid @Override public boolean isNoTerminologyServer() { - return myWrap.isNoTerminologyServer(); + return true; } @Override From cefacdc18ce92687b6ed6e685c7602bf380fb7cd Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Fri, 30 Nov 2018 09:34:49 +0200 Subject: [PATCH 28/41] Returned to original state as the experiment failed --- .../hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index e440315a803..c09e9ea9115 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -637,7 +637,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid @Override public boolean isNoTerminologyServer() { - return true; + return myWrap.isNoTerminologyServer(); } @Override From a0db7e20b69b2d8b1d61382b5d10a8ee1e05db15 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Mon, 3 Dec 2018 13:48:40 +0200 Subject: [PATCH 29/41] temporarily set hard coded error when no profile found --- .../hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index c09e9ea9115..967e8f55241 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -249,6 +249,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid v.setResourceIdRule(IdStatus.OPTIONAL); v.setNoTerminologyChecks(isNoTerminologyChecks()); v.setMyEnableWhenEvaluator(enableWhenEvaluatorSupplier.apply(wrappedWorkerContext)); + v.setErrorForUnknownProfiles(true); // TODO must be configurable instead of hardcoded to be always true List messages = new ArrayList<>(); From c2b3d48afe85a1b3c13850a4ad073c4c7adadf4a Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Fri, 7 Dec 2018 16:33:09 +0200 Subject: [PATCH 30/41] Support required questionnaireitem groups --- .../fhir/r4/validation/InstanceValidator.java | 8 +- ...estionnaireResponseValidatorDstu3Test.java | 95 ++++++++++++++----- 2 files changed, 75 insertions(+), 28 deletions(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 6e2a2c84846..101da940c23 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2639,9 +2639,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List answers = new ArrayList(); element.getNamedChildren("answer", answers); if (inProgress) - warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId()); + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item "+qItem.getLinkId()); else - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId()); + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item "+qItem.getLinkId()); if (answers.size() > 1) rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed"); @@ -2729,6 +2729,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } +private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, List answers) { + return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; +} + private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) { if (elements.size() > 1) rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); 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 463bec25368..7a535344c0e 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 @@ -22,6 +22,7 @@ import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemC import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.util.ArrayList; @@ -40,17 +41,23 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; public class QuestionnaireResponseValidatorDstu3Test { + private static final String QUESTIONNAIRE_URL = "http://example.com/Questionnaire/q1"; public static final IdType ID_ICC_QUESTIONNAIRE_SETUP = new IdType("Questionnaire/profile"); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireResponseValidatorDstu3Test.class); private static final String CODE_ICC_SCHOOLTYPE_PT = "PT"; private static final IdType ID_VS_SCHOOLTYPE = new IdType("ValueSet/schooltype"); private static final String SYSTEMURI_ICC_SCHOOLTYPE = "http://ehealthinnovation/icc/ns/schooltype"; private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(); - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static FhirContext ourCtx; private FhirInstanceValidator myInstanceVal; private FhirValidator myVal; private IValidationSupport myValSupport; + @BeforeClass + public static void beforeClass() { + ourCtx = FhirContext.forDstu3(); + } + @Before public void before() { myValSupport = mock(IValidationSupport.class); @@ -122,7 +129,7 @@ public class QuestionnaireResponseValidatorDstu3Test { answerValues[11] = new Coding().setSystem("http://codesystems.com/system").setCode("code0"); answerValues[12] = new StringType("some value"); answerValues[13] = new Attachment().setData("some data".getBytes()).setContentType("txt"); - answerValues[14] = new Reference("http://example.com/Questionnaire/q1"); + answerValues[14] = new Reference(QUESTIONNAIRE_URL); answerValues[15] = new Quantity(42); for (int i = 0; i < itemCnt; i++) { @@ -132,7 +139,7 @@ public class QuestionnaireResponseValidatorDstu3Test { reset(myValSupport); Questionnaire q = new Questionnaire(); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), - eq("http://example.com/Questionnaire/q1"))).thenReturn(q); + eq(QUESTIONNAIRE_URL))).thenReturn(q); when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system"))).thenReturn(codeSystem); when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options); myInstanceVal.flushCaches(); @@ -149,7 +156,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.INPROGRESS); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId(linkId).addAnswer().setValue(answerValues[i]); ValidationResult errors = myVal.validateWithResult(qa); @@ -165,7 +172,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); @@ -178,11 +185,11 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testCodedAnswer() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; Questionnaire q = new Questionnaire(); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setOptions(new Reference("http://somevalueset")); - when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq("http://example.com/Questionnaire/q1"))).thenReturn(q); + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(QUESTIONNAIRE_URL))).thenReturn(q); CodeSystem codeSystem = new CodeSystem(); codeSystem.setContent(CodeSystemContentMode.COMPLETE); @@ -246,7 +253,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); QuestionnaireResponseItemComponent qaGroup = qa.addItem(); qaGroup.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); @@ -256,6 +263,43 @@ public class QuestionnaireResponseValidatorDstu3Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString("No LinkId, so can't be validated")); } + + @Test + public void testMissingAnswerInNestedStructureIsReported() throws Exception { + Questionnaire q = new Questionnaire(); + q.addItem().setType(QuestionnaireItemType.GROUP).setRequired(true) + .addItem().setType(QuestionnaireItemType.GROUP).setRequired(true) + .addItem().setType(QuestionnaireItemType.BOOLEAN).setLinkId("link0").setRequired(true); + + QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); + + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); + + ValidationResult errors = myVal.validateWithResult(qa); + + assertThat(errors.toString(), Matchers.not(containsString("No issues"))); + } + + @Test + public void testGroupMarkedAsRequiredIsOk() throws Exception { + Questionnaire q = new Questionnaire(); + q.addItem().setType(QuestionnaireItemType.GROUP).setRequired(true).setLinkId("link1") + .addItem().setType(QuestionnaireItemType.BOOLEAN).setLinkId("link0").setRequired(true); + + QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); + qa.addItem().setLinkId("link1") + .addItem().setLinkId("link0").addAnswer().setValue(new BooleanType(true)); + + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); + + ValidationResult errors = myVal.validateWithResult(qa); + + assertThat(errors.toString(), containsString("No issues")); + } @Test public void testItemWithNoType() { @@ -266,7 +310,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); QuestionnaireResponseItemComponent qaGroup = qa.addItem().setLinkId("link0"); qaGroup.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); @@ -287,7 +331,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); String reference = qa.getQuestionnaire().getReference(); @@ -316,8 +360,7 @@ public class QuestionnaireResponseValidatorDstu3Test { 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.getQuestionnaire().setReference(QUESTIONNAIRE_URL); String reference = qa.getQuestionnaire().getReference(); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); @@ -345,7 +388,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); String reference = qa.getQuestionnaire().getReference(); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); @@ -372,7 +415,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link0").addAnswer().setValue(new Quantity().setValue(1L)); String reference = qa.getQuestionnaire().getReference(); @@ -400,7 +443,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link0").addAnswer().setValue(new Quantity().setValue(1L)); String reference = qa.getQuestionnaire().getReference(); @@ -428,7 +471,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("BAR")); @@ -458,7 +501,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); // link1 should be disabled, because the enableWhen enables it when link0 doesn't haven an answer qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); @@ -479,7 +522,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qr = new QuestionnaireResponse(); qr.setStatus(QuestionnaireResponseStatus.COMPLETED); - qr.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qr.addItem().setLinkId("link2").addAnswer().setValue(new StringType("FOO")); @@ -499,7 +542,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qr = new QuestionnaireResponse(); qr.setStatus(QuestionnaireResponseStatus.COMPLETED); - qr.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qr.addItem().setLinkId("link0").setText("Text"); qr.addItem().setLinkId("link1").addAnswer().setValue(new StringType("Answer")); String reference = qr.getQuestionnaire().getReference(); @@ -514,7 +557,7 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testEmbeddedItemInChoice() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; String valueSetRef = "http://somevalueset"; String codeSystemUrl = "http://codesystems.com/system"; String codeValue = "code0"; @@ -570,7 +613,7 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testEmbeddedItemInOpenChoice() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; String valueSetRef = "http://somevalueset"; String codeSystemUrl = "http://codesystems.com/system"; String codeValue = "code0"; @@ -626,7 +669,7 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testEmbeddedItemInString() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; // create the questionnaire QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); @@ -727,7 +770,7 @@ public class QuestionnaireResponseValidatorDstu3Test { .setType(QuestionnaireItemType.STRING) .setRequired(true); - String reference = "http://example.com/Questionnaire/q1"; + String reference = QUESTIONNAIRE_URL; when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))) .thenReturn(q); @@ -751,7 +794,7 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testOpenchoiceAnswer() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; Questionnaire q = new Questionnaire(); QuestionnaireItemComponent item = q.addItem(); @@ -874,7 +917,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); @@ -892,7 +935,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link1").addItem().setLinkId("link2"); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); From 755eaa5f25a7af921cbca6e1a2a0efdce183cd34 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Tue, 11 Dec 2018 11:17:40 +0200 Subject: [PATCH 31/41] bumped version n. for release --- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 88d7306044a..86b2f1f7fb4 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -12,7 +12,7 @@ fi.kela.kanta.phr hapi-fhir-converter bundle - 3.6.1-PHRFIX-SNAPSHOT + 3.6.1-PHRFIX diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 8833a2c3b95..4bad5831721 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -11,7 +11,7 @@ hapi-fhir-validation bundle - 3.6.1-PHRFIX-SNAPSHOT + 3.6.1-PHRFIX HAPI FHIR - Validation @@ -29,7 +29,7 @@ fi.kela.kanta.phr hapi-fhir-converter - 3.6.1-PHRFIX-SNAPSHOT + 3.6.1-PHRFIX From a1f79a2063a6f6204d9c2d253a90d977ced440a9 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Wed, 12 Dec 2018 14:22:38 +0200 Subject: [PATCH 32/41] Filter Extensions from enablewhen evaluation --- hapi-fhir-validation/pom.xml | 2 +- .../hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 4bad5831721..b4d75655adc 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -11,7 +11,7 @@ hapi-fhir-validation bundle - 3.6.1-PHRFIX + 3.6.2-PHRFIX HAPI FHIR - Validation diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java index 8f296eedb84..0b23e006564 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java @@ -143,8 +143,13 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { return item.getChildrenByName(ANSWER_ELEMENT) .stream() .flatMap(c -> c.getChildren().stream()) + .filter(DefaultEnableWhenEvaluator::notExtension) .collect(Collectors.toList()); } + + private static boolean notExtension(Element e) { + return !Extension.class.isAssignableFrom(e.getClass()); + } private boolean compareCodingAnswer(Coding expectedAnswer, Coding actualAnswer, QuestionnaireItemOperator questionnaireItemOperator) { boolean result = compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer); From dd8e027ff44f9fdbb08aa45f9ba06a98fec021c7 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Thu, 13 Dec 2018 12:12:56 +0200 Subject: [PATCH 33/41] Don't evaluate Extensions when determining type of answer --- hapi-fhir-validation/pom.xml | 2 +- .../DefaultEnableWhenEvaluator.java | 31 +++++++++++++---- ...estionnaireResponseValidatorDstu3Test.java | 33 ++++++++++++++++++- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index b4d75655adc..2d48adf7cff 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -11,7 +11,7 @@ hapi-fhir-validation bundle - 3.6.2-PHRFIX + 3.6.3-PHRFIX HAPI FHIR - Validation diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java index 0b23e006564..4a8981518f8 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java @@ -65,11 +65,33 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { .anyMatch(answer -> evaluateAnswer(answer, enableCondition.getAnswer(), enableCondition.getOperator())); return new EnableWhenResult(result, linkId, enableCondition, questionnaireResponse); } + + public Type convertToType(Element element) { + Type b = new Factory().create(element.fhirType()); + if (b instanceof PrimitiveType) { + ((PrimitiveType) b).setValueAsString(element.primitiveValue()); + } else { + for (Element child : element.getChildren()) { + if (!isExtension(child)) { + b.setProperty(child.getName(), convertToType(child)); + } + } + } + return b; + } + + + private boolean isExtension(Element element) { + return "Extension".equals(element.fhirType()); + } protected boolean evaluateAnswer(Element answer, Type expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) { Type actualAnswer; + if (isExtension(answer)) { + return false; + } try { - actualAnswer = answer.asType(); + actualAnswer = convertToType(answer); } catch (FHIRException e) { throw new UnprocessableEntityException("Unexpected answer type", e); } @@ -81,7 +103,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { } else if ((expectedAnswer instanceof PrimitiveType)) { return comparePrimitiveAnswer((PrimitiveType)actualAnswer, (PrimitiveType)expectedAnswer, questionnaireItemOperator); } else if (expectedAnswer instanceof Quantity) { - return compareQuantityAnswer((Quantity)expectedAnswer, (Quantity)actualAnswer, questionnaireItemOperator); + return compareQuantityAnswer((Quantity)actualAnswer, (Quantity)expectedAnswer, questionnaireItemOperator); } // TODO: Attachment, reference? throw new UnprocessableEntityException("Unimplemented answer type: " + expectedAnswer.getClass()); @@ -143,14 +165,9 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { return item.getChildrenByName(ANSWER_ELEMENT) .stream() .flatMap(c -> c.getChildren().stream()) - .filter(DefaultEnableWhenEvaluator::notExtension) .collect(Collectors.toList()); } - private static boolean notExtension(Element e) { - return !Extension.class.isAssignableFrom(e.getClass()); - } - private boolean compareCodingAnswer(Coding expectedAnswer, Coding actualAnswer, QuestionnaireItemOperator questionnaireItemOperator) { boolean result = compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer); if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){ 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 7a535344c0e..44f4eaab0fe 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 @@ -18,6 +18,7 @@ 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.QuestionnaireResponseItemAnswerComponent; import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.junit.AfterClass; @@ -33,6 +34,9 @@ import java.util.List; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasSize; +import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.BOOLEAN; +import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.CHOICE; +import static org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; @@ -554,7 +558,34 @@ public class QuestionnaireResponseValidatorDstu3Test { errors.getMessages().stream().filter(vm -> vm.getMessage().contains("Structural Error")) .anyMatch(vm -> vm.getMessage().contains("link1"))); } - + + @Test + public void testAnswerIsValueCodingWithExtensionInside() throws Exception { + Questionnaire q = new Questionnaire(); + Coding qcoding = new Coding(); + qcoding.setCode("1293"); + q.addItem().setLinkId("1B").setRequired(true).setType(CHOICE).addOption().setValue(qcoding); + q.addItem().setLinkId("2B").setType(BOOLEAN).addEnableWhen().setQuestion("1B").setAnswer(qcoding); + + QuestionnaireResponse qr = new QuestionnaireResponse(); + qr.setStatus(COMPLETED); + qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL); + QuestionnaireResponseItemComponent qrItem = qr.addItem().setLinkId("1B"); + Coding coding = new Coding(); + coding.setCode("1293"); + QuestionnaireResponseItemAnswerComponent answer = qrItem.addAnswer(); + answer.setValue(coding); + coding.addExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", new BooleanType(true)); + qr.addItem().setLinkId("2B").addAnswer().setValue(new BooleanType(true)); + + String reference = qr.getQuestionnaire().getReference(); + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); + + ValidationResult errors = myVal.validateWithResult(qr); + assertThat(errors.toString(), containsString("No issues")); + + } + @Test public void testEmbeddedItemInChoice() { String questionnaireRef = QUESTIONNAIRE_URL; From 58959eec4856846f58727063e600bccceaa73cf2 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Thu, 13 Dec 2018 13:29:17 +0200 Subject: [PATCH 34/41] Do not force enableWhen to have system --- hapi-fhir-validation/pom.xml | 2 +- .../DefaultEnableWhenEvaluator.java | 2 +- ...estionnaireResponseValidatorDstu3Test.java | 29 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 2d48adf7cff..445aadcb072 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -11,7 +11,7 @@ hapi-fhir-validation bundle - 3.6.3-PHRFIX + 3.6.4-PHRFIX HAPI FHIR - Validation diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java index 4a8981518f8..d070570c816 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/DefaultEnableWhenEvaluator.java @@ -189,7 +189,7 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator { } private boolean compareSystems(Coding expectedCoding, Coding value) { - if (expectedCoding.hasSystem() != value.hasSystem()) { + if (expectedCoding.hasSystem() && !value.hasSystem()) { return false; } if (expectedCoding.hasSystem()) { 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 44f4eaab0fe..9c9eb9ea785 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 @@ -586,6 +586,35 @@ public class QuestionnaireResponseValidatorDstu3Test { } + @Test + public void testChoiceItemsEnableWhenHasNoSystemYetAnswerHasSystem() throws Exception { + Questionnaire q = new Questionnaire(); + Coding qcoding = new Coding(); + qcoding.setCode("male"); + qcoding.setSystem("http://hl7.org/fhir/administrative-gender"); + q.addItem().setLinkId("1B").setRequired(true).setType(CHOICE).addOption().setValue(qcoding); + Coding enablewhenCoding = new Coding(); + enablewhenCoding.setCode("male"); + q.addItem().setLinkId("2B").setType(BOOLEAN).addEnableWhen().setQuestion("1B").setAnswer(enablewhenCoding); + + QuestionnaireResponse qr = new QuestionnaireResponse(); + qr.setStatus(COMPLETED); + qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL); + QuestionnaireResponseItemComponent qrItem = qr.addItem().setLinkId("1B"); + Coding coding = new Coding(); + coding.setCode("male"); + coding.setSystem("http://hl7.org/fhir/administrative-gender"); + QuestionnaireResponseItemAnswerComponent answer = qrItem.addAnswer(); + answer.setValue(coding); + qr.addItem().setLinkId("2B").addAnswer().setValue(new BooleanType(true)); + + String reference = qr.getQuestionnaire().getReference(); + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); + + ValidationResult errors = myVal.validateWithResult(qr); + assertThat(errors.toString(), containsString("No issues")); + } + @Test public void testEmbeddedItemInChoice() { String questionnaireRef = QUESTIONNAIRE_URL; From 8183c18406a935176c8ff8e4d1d82cfa6b252abb Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Mon, 17 Dec 2018 13:54:43 +0200 Subject: [PATCH 35/41] Corrected project dependency version numberts --- hapi-fhir-converter/pom.xml | 21 ++++++++++----------- hapi-fhir-validation/pom.xml | 27 +++++++++++++-------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 86b2f1f7fb4..77489345dbb 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -12,20 +12,19 @@ fi.kela.kanta.phr hapi-fhir-converter bundle - 3.6.1-PHRFIX ca.uhn.hapi.fhir hapi-fhir-base - 3.7.0-SNAPSHOT + ${project.version} ca.uhn.hapi.fhir hapi-fhir-server - 3.7.0-SNAPSHOT + ${project.version} true @@ -37,43 +36,43 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 3.7.0-SNAPSHOT + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 3.7.0-SNAPSHOT + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-structures-dstu2.1 - 3.7.0-SNAPSHOT + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 3.7.0-SNAPSHOT + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 3.7.0-SNAPSHOT + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 3.7.0-SNAPSHOT + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 3.7.0-SNAPSHOT + ${project.version} true @@ -86,7 +85,7 @@ ca.uhn.hapi.fhir hapi-fhir-client - 3.6.0 + ${project.version} test diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 445aadcb072..b6df3c95f47 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -11,7 +11,6 @@ hapi-fhir-validation bundle - 3.6.4-PHRFIX HAPI FHIR - Validation @@ -19,17 +18,17 @@ ca.uhn.hapi.fhir hapi-fhir-base - 3.6.0 + ${project.version} ca.uhn.hapi.fhir hapi-fhir-utilities - 3.6.0 + ${project.version} fi.kela.kanta.phr hapi-fhir-converter - 3.6.1-PHRFIX + ${project.version} @@ -64,49 +63,49 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 3.6.0 + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 3.6.0 + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-structures-dstu2.1 - 3.6.0 + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 3.6.0 + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 3.6.0 + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 3.6.0 + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 3.6.0 + ${project.version} true ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 3.6.0 + ${project.version} true @@ -119,13 +118,13 @@ ca.uhn.hapi.fhir hapi-fhir-server - 3.6.0 + ${project.version} test ca.uhn.hapi.fhir hapi-fhir-client - 3.6.0 + ${project.version} test From 89e3cac0e284e83522cf4627112f28c7f2a6713e Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Mon, 17 Dec 2018 13:55:15 +0200 Subject: [PATCH 36/41] Corrected problems caused by merge --- .../QuestionnaireResponseValidatorDstu3Test.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) 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 9c9eb9ea785..e50b14f40bc 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 @@ -347,20 +347,12 @@ public class QuestionnaireResponseValidatorDstu3Test { } @Test - public void testRequiredQuestionWithEnableWhenHidesQuestionHasAnswerTrue() { + public void testEnableWhenWithHasAnswerTrueDisablesQuestionWhenNoAnswerIsPresent() { Questionnaire q = new Questionnaire(); - q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); + q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING); + q.addItem().setLinkId("link1").setRequired(true).addEnableWhen().setQuestion("link0").setHasAnswer(true); - // 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); - QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); From 7ad45308e082659dc11727581af49df481750713 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Mon, 17 Dec 2018 14:10:15 +0200 Subject: [PATCH 37/41] Make this configurable --- .../dstu3/hapi/validation/FhirInstanceValidator.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index 967e8f55241..30e7a73d1ee 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -65,6 +65,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid private volatile WorkerContextWrapper myWrappedWorkerContext; private Function enableWhenEvaluatorSupplier = ctx -> new DefaultEnableWhenEvaluator(); + private boolean errorForUnknownProfiles; + /** * Constructor *

@@ -190,6 +192,14 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid public boolean isAnyExtensionsAllowed() { return myAnyExtensionsAllowed; } + + public boolean isErrorForUnknownProfiles() { + return errorForUnknownProfiles; + } + + public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) { + this.errorForUnknownProfiles = errorForUnknownProfiles; + } /** * If set to {@literal true} (default is true) extensions which are not known to the @@ -249,7 +259,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid v.setResourceIdRule(IdStatus.OPTIONAL); v.setNoTerminologyChecks(isNoTerminologyChecks()); v.setMyEnableWhenEvaluator(enableWhenEvaluatorSupplier.apply(wrappedWorkerContext)); - v.setErrorForUnknownProfiles(true); // TODO must be configurable instead of hardcoded to be always true + v.setErrorForUnknownProfiles(isErrorForUnknownProfiles()); List messages = new ArrayList<>(); From 01eb7faf66730e6cf5cc978491ddf25c33028077 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Tue, 18 Dec 2018 10:15:47 +0200 Subject: [PATCH 38/41] Deleted unnecessary Inteface class --- .../validation/IEnableWhenEvaluator.java | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java deleted file mode 100644 index 10501484723..00000000000 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/IEnableWhenEvaluator.java +++ /dev/null @@ -1,18 +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; -import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; -import org.hl7.fhir.r4.elementmodel.Element; - -public interface IEnableWhenEvaluator { - - public boolean isQuestionEnabled(QuestionnaireItemComponent item, List theResponseItems); - - - public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent qItem, - Element element); - -} From a3e7f5b18f50817dd70bb8ca9011bbafed0519c3 Mon Sep 17 00:00:00 2001 From: Matti Uusitalo Date: Tue, 18 Dec 2018 11:21:29 +0200 Subject: [PATCH 39/41] Corrected group ids back to ca.uhn.hapi.fhir --- hapi-fhir-converter/pom.xml | 1 - hapi-fhir-validation/pom.xml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 77489345dbb..c9aa6d46be1 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -9,7 +9,6 @@ ../hapi-deployable-pom/pom.xml - fi.kela.kanta.phr hapi-fhir-converter bundle diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index b6df3c95f47..6723bc84d70 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -26,7 +26,7 @@ ${project.version} - fi.kela.kanta.phr + ca.uhn.hapi.fhir hapi-fhir-converter ${project.version} From 9a8e3245b8d1f4ebd7ef819c325eac94b59edccd Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 5 Feb 2019 15:00:36 -0500 Subject: [PATCH 40/41] Build fixes --- .../hl7/fhir/convertors/VersionConvertor_30_40.java | 13 +++---------- .../java/ca/uhn/fhir/rest/server/SearchR4Test.java | 8 ++++---- .../hapi/validation/FhirInstanceValidator.java | 8 ++++---- .../hl7/fhir/r4/validation/InstanceValidator.java | 2 +- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java index 579acd9a82a..eeaa6af11aa 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java @@ -32,16 +32,9 @@ import org.hl7.fhir.dstu3.model.ExpansionProfile.SystemVersionProcessingMode; import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Enumeration; +import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.Expression.ExpressionLanguage; import org.hl7.fhir.r4.model.HealthcareService.HealthcareServiceEligibilityComponent; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Reference; -import org.hl7.fhir.r4.model.TerminologyCapabilities; -import org.hl7.fhir.r4.model.Type; -import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.utilities.Utilities; @@ -76,7 +69,7 @@ import org.hl7.fhir.utilities.Utilities; public class VersionConvertor_30_40 { - private static List CANONICAL_URLS = new ArrayList(); + private static List CANONICAL_URLS = new ArrayList<>(); static { CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-conceptmap"); CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-valueset"); @@ -16013,7 +16006,7 @@ public class VersionConvertor_30_40 { tgt.setType(convertQuestionnaireItemType(src.getType())); for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent t : src.getEnableWhen()) tgt.addEnableWhen(convertQuestionnaireItemEnableWhenComponent(t)); - tgt.setEnableBehavior(EnableWhenBehavior.ANY); + tgt.setEnableBehavior(Questionnaire.EnableWhenBehavior.ANY); if (src.hasRequired()) tgt.setRequired(src.getRequired()); if (src.hasRepeats()) diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java index cae6b2debd8..d57263707de 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java @@ -95,7 +95,7 @@ public class SearchR4Test { linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl(); assertThat(linkSelf, containsString("_elements=name")); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_elements=meta,name")); + assertThat(linkNext, containsString("_elements=name")); ourLog.info(toJson(bundle)); @@ -104,7 +104,7 @@ public class SearchR4Test { bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); assertThat(toJson(bundle), not(containsString("\"active\""))); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_elements=meta,name")); + assertThat(linkNext, containsString("_elements=name")); assertThat(linkNext, containsString("_elements:exclude=active,birthDate")); // Fetch the next page @@ -112,7 +112,7 @@ public class SearchR4Test { bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); assertThat(toJson(bundle), not(containsString("\"active\""))); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_elements=meta,name")); + assertThat(linkNext, containsString("_elements=name")); assertThat(linkNext, containsString("_elements:exclude=active,birthDate")); // Fetch the next page @@ -120,7 +120,7 @@ public class SearchR4Test { bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); assertThat(toJson(bundle), not(containsString("\"active\""))); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertThat(linkNext, containsString("_elements=meta,name")); + assertThat(linkNext, containsString("_elements=name")); assertThat(linkNext, containsString("_elements:exclude=active,birthDate")); } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index 725d085dc49..fa7b2f4c83a 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -63,7 +63,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid private IValidationSupport myValidationSupport; private boolean noTerminologyChecks = false; private volatile WorkerContextWrapper myWrappedWorkerContext; - private Function enableWhenEvaluatorSupplier = ctx -> new DefaultEnableWhenEvaluator(); + private Function enableWhenEvaluatorSupplier; private boolean errorForUnknownProfiles; private List extensionDomains = Collections.emptyList(); @@ -86,6 +86,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid myDocBuilderFactory = DocumentBuilderFactory.newInstance(); myDocBuilderFactory.setNamespaceAware(true); myValidationSupport = theValidationSupport; + setEnableWhenEvaluatorSupplier(ctx -> new DefaultEnableWhenEvaluator()); } /** @@ -254,7 +255,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid /** * Sets a customized {@link IEnableWhenEvaluator} which is injected to created InstanceValidators - * @param myEnableWhenEvaluator */ public void setEnableWhenEvaluatorSupplier( Function enableWhenEvaluatorSupplier) { @@ -293,9 +293,9 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid v.setAnyExtensionsAllowed(isAnyExtensionsAllowed()); v.setResourceIdRule(IdStatus.OPTIONAL); v.setNoTerminologyChecks(isNoTerminologyChecks()); - v.setMyEnableWhenEvaluator(enableWhenEvaluatorSupplier.apply(wrappedWorkerContext)); + v.setEnableWhenEvaluator(enableWhenEvaluatorSupplier.apply(wrappedWorkerContext)); v.setErrorForUnknownProfiles(isErrorForUnknownProfiles()); - v.addExtensionDomains(extensionDomains); + v.getExtensionDomains().addAll(extensionDomains); List messages = new ArrayList<>(); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 1821523b812..2919b7bd5da 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2469,7 +2469,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat this.allowXsiLocation = allowXsiLocation; } - public void setMyEnableWhenEvaluator(IEnableWhenEvaluator myEnableWhenEvaluator) { + public void setEnableWhenEvaluator(IEnableWhenEvaluator myEnableWhenEvaluator) { this.myEnableWhenEvaluator = myEnableWhenEvaluator; } From e3dd296db7bc8c8f36bbee99c56b593aa09cbd93 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 5 Feb 2019 15:36:21 -0500 Subject: [PATCH 41/41] Fix flaky test hopefully? --- .../subscription/resthook/RestHookWithInterceptorR4Test.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithInterceptorR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithInterceptorR4Test.java index d796eadd75b..5554aa28ef3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithInterceptorR4Test.java @@ -144,6 +144,9 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test { interceptor.getFinishedLatch().await(10, TimeUnit.SECONDS); ResourceDeliveryMessage lastDelivery = interceptor.getLastDelivery(); + assertTrue(lastDelivery.getAttribute("ATTR1").isPresent()); + assertTrue(lastDelivery.getAttribute("ATTR2").isPresent()); + assertTrue(lastDelivery.getAttribute("ATTRBLANK").isPresent()); assertEquals("Some value 1", lastDelivery.getAttribute("ATTR1").get()); assertEquals("Some value 2", lastDelivery.getAttribute("ATTR2").get()); assertEquals("", lastDelivery.getAttribute("ATTRBLANK").get());