Fix validation issue with open-choice questions in R4 questionnaires
This commit is contained in:
parent
56965820ea
commit
3b8b2a94c3
|
@ -0,0 +1,37 @@
|
||||||
|
package org.hl7.fhir.convertors.conv40_50;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
|
||||||
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class Questionnaire40_50Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Test r5 -> r4 Questionnaire conversion.")
|
||||||
|
public void testR5_R4() throws IOException {
|
||||||
|
InputStream r4_input = this.getClass().getResourceAsStream("/q_open_40.json");
|
||||||
|
String source = TextFile.streamToString(r4_input);
|
||||||
|
System.out.println(source);
|
||||||
|
|
||||||
|
org.hl7.fhir.r4.model.Questionnaire r4_actual = (org.hl7.fhir.r4.model.Questionnaire) new org.hl7.fhir.r4.formats.JsonParser().parse(source);
|
||||||
|
org.hl7.fhir.r5.model.Resource r5_conv = VersionConvertorFactory_40_50.convertResource(r4_actual);
|
||||||
|
|
||||||
|
org.hl7.fhir.r5.formats.JsonParser r5_parser = new org.hl7.fhir.r5.formats.JsonParser();
|
||||||
|
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
r5_parser.compose(stream, r5_conv);
|
||||||
|
|
||||||
|
org.hl7.fhir.r5.model.Resource r5_streamed = (org.hl7.fhir.r5.model.Questionnaire) new org.hl7.fhir.r5.formats.JsonParser().parse(new ByteArrayInputStream(stream.toByteArray()));
|
||||||
|
org.hl7.fhir.r4.model.Resource r4_conv = VersionConvertorFactory_40_50.convertResource(r5_streamed);
|
||||||
|
|
||||||
|
assertTrue(r4_actual.equalsDeep(r4_conv), "should be the same");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
{"resourceType": "Questionnaire",
|
||||||
|
"id": "ed364266b937bb3bd73082b1",
|
||||||
|
"item": [
|
||||||
|
{
|
||||||
|
"extension": [
|
||||||
|
{
|
||||||
|
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
|
||||||
|
"valueCodeableConcept": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"code": "editableDropdown"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "specimen-source",
|
||||||
|
"answerOption": [
|
||||||
|
{
|
||||||
|
"valueCoding": {
|
||||||
|
"code": "U",
|
||||||
|
"display": "Urine"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"valueCoding": {
|
||||||
|
"code": "B",
|
||||||
|
"display": "Blood"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"valueCoding": {
|
||||||
|
"code": "S",
|
||||||
|
"display": "Saliva"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"code": [
|
||||||
|
{
|
||||||
|
"code": "specimen-source"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"linkId": "specimen-source",
|
||||||
|
"text": "Source of specimen",
|
||||||
|
"type": "open-choice"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Test Open Choice question",
|
||||||
|
"status": "active",
|
||||||
|
"subjectType": [
|
||||||
|
"Patient"
|
||||||
|
]
|
||||||
|
}
|
|
@ -514,6 +514,7 @@ public class I18nConstants {
|
||||||
public static final String QUESTIONNAIRE_QR_ITEM_ONLYONEI = "Questionnaire_QR_Item_OnlyOneI";
|
public static final String QUESTIONNAIRE_QR_ITEM_ONLYONEI = "Questionnaire_QR_Item_OnlyOneI";
|
||||||
public static final String QUESTIONNAIRE_QR_ITEM_ORDER = "Questionnaire_QR_Item_Order";
|
public static final String QUESTIONNAIRE_QR_ITEM_ORDER = "Questionnaire_QR_Item_Order";
|
||||||
public static final String QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS = "Questionnaire_QR_Item_StringNoOptions";
|
public static final String QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS = "Questionnaire_QR_Item_StringNoOptions";
|
||||||
|
public static final String QUESTIONNAIRE_QR_ITEM_STRING_IN_CODING = "QUESTIONNAIRE_QR_ITEM_STRING_IN_CODING";
|
||||||
public static final String QUESTIONNAIRE_QR_ITEM_TEXT = "Questionnaire_QR_Item_Text";
|
public static final String QUESTIONNAIRE_QR_ITEM_TEXT = "Questionnaire_QR_Item_Text";
|
||||||
public static final String QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS = "Questionnaire_QR_Item_TimeNoOptions";
|
public static final String QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS = "Questionnaire_QR_Item_TimeNoOptions";
|
||||||
public static final String QUESTIONNAIRE_QR_ITEM_WRONGTYPE = "Questionnaire_QR_Item_WrongType";
|
public static final String QUESTIONNAIRE_QR_ITEM_WRONGTYPE = "Questionnaire_QR_Item_WrongType";
|
||||||
|
|
|
@ -524,6 +524,7 @@ Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type
|
||||||
Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date
|
Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date
|
||||||
Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer
|
Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer
|
||||||
Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string
|
Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string
|
||||||
|
QUESTIONNAIRE_QR_ITEM_STRING_IN_CODING = The string value ''{0}'' matches an entry in the list of valid Codings, so this is probably an error
|
||||||
Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time
|
Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time
|
||||||
Questionnaire_QR_Item_NoString = The string {0} is not a valid option
|
Questionnaire_QR_Item_NoString = The string {0} is not a valid option
|
||||||
Questionnaire_QR_Item_NoTime = The time {0} is not a valid option
|
Questionnaire_QR_Item_NoTime = The time {0} is not a valid option
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.hl7.fhir.r5.model.Coding;
|
||||||
import org.hl7.fhir.r5.model.DateType;
|
import org.hl7.fhir.r5.model.DateType;
|
||||||
import org.hl7.fhir.r5.model.IntegerType;
|
import org.hl7.fhir.r5.model.IntegerType;
|
||||||
import org.hl7.fhir.r5.model.Questionnaire;
|
import org.hl7.fhir.r5.model.Questionnaire;
|
||||||
|
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireAnswerConstraint;
|
||||||
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
|
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
|
||||||
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
|
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
|
||||||
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType;
|
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType;
|
||||||
|
@ -812,7 +813,7 @@ public class QuestionnaireValidator extends BaseValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkOption(List<ValidationMessage> errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, String type) {
|
private boolean checkOption(List<ValidationMessage> errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, String type) {
|
||||||
return checkOption(errors, answer, stack, qSrc, qItem, type, false);
|
return checkOption(errors, answer, stack, qSrc, qItem, type, qItem.getAnswerConstraint() == QuestionnaireAnswerConstraint.OPTIONSORSTRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkOption(List<ValidationMessage> errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) {
|
private boolean checkOption(List<ValidationMessage> errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) {
|
||||||
|
@ -928,31 +929,29 @@ public class QuestionnaireValidator extends BaseValidator {
|
||||||
Element v = answer.getNamedChild("valueString", false);
|
Element v = answer.getNamedChild("valueString", false);
|
||||||
NodeStack ns = stack.push(v, -1, null, null);
|
NodeStack ns = stack.push(v, -1, null, null);
|
||||||
if (qItem.getAnswerOption().size() > 0) {
|
if (qItem.getAnswerOption().size() > 0) {
|
||||||
List<StringType> list = new ArrayList<StringType>();
|
boolean found = false;
|
||||||
|
boolean empty = true;
|
||||||
for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) {
|
for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) {
|
||||||
try {
|
if (components.getValue() != null && components.hasValueStringType()) {
|
||||||
if (components.getValue() != null) {
|
empty = false;
|
||||||
list.add(components.getValueStringType());
|
found = found || components.getValue().primitiveValue().equals((v.primitiveValue()));
|
||||||
}
|
|
||||||
} catch (FHIRException e) {
|
|
||||||
// If it's the wrong type, just keep going
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!openChoice) {
|
if (!openChoice) {
|
||||||
if (list.isEmpty()) {
|
if (empty) {
|
||||||
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSSTRING) && ok;
|
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSSTRING) && ok;
|
||||||
} else {
|
} else {
|
||||||
boolean found = false;
|
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOSTRING, v.primitiveValue()) && ok;
|
||||||
for (StringType item : list) {
|
}
|
||||||
if (item.getValue().equals((v.primitiveValue()))) {
|
} else {
|
||||||
found = true;
|
found = false;
|
||||||
break;
|
for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) {
|
||||||
}
|
if (components.getValue() != null && components.hasValueCoding()) {
|
||||||
}
|
Coding c = components.getValueCoding();
|
||||||
if (!found) {
|
found = found || (c.hasDisplay() && c.getDisplay().equalsIgnoreCase(v.primitiveValue())) || (c.hasCode() && c.getCode().equalsIgnoreCase(v.primitiveValue()));
|
||||||
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOSTRING, v.primitiveValue()) && ok;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), !found, I18nConstants.QUESTIONNAIRE_QR_ITEM_STRING_IN_CODING, v.primitiveValue()) && ok;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hint(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS);
|
hint(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS);
|
||||||
|
|
Loading…
Reference in New Issue