From db9560f3f09e29ffd51d69cdc92235af4204c110 Mon Sep 17 00:00:00 2001 From: Reto Wettstein Date: Tue, 28 Oct 2025 21:45:24 +0100 Subject: [PATCH 01/11] Quantity type for Task.input/output and QuestionnaireResponse.item.answer and Questionnaire.item.type.choice --- .../QuestionnaireResponseHelperImpl.java | 5 + .../QuestionnaireResponseHelperImpl.java | 3 + .../fhir/adapter/ElementQuantityValue.java | 60 +++++ .../ResourceQuestionnaireResponse.java | 38 +-- .../dev/dsf/fhir/adapter/ResourceTask.java | 36 ++- .../src/main/resources/fhir/static/dsf.css | 24 +- .../src/main/resources/fhir/static/form.css | 2 +- .../src/main/resources/fhir/static/form.js | 110 ++++++++- .../fhir/template/resourceElements.html | 13 ++ .../resourceQuestionnaireResponse.html | 32 +++ .../resources/fhir/template/resourceTask.html | 86 +++++++ .../dsf-questionnaire-2.0.0.xml | 220 ++++++++++++++++++ .../dsf-questionnaire-2.0.0.xml.post | 1 + .../dsf-questionnaire-response-2.0.0.xml | 6 + 14 files changed, 593 insertions(+), 43 deletions(-) create mode 100644 dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ElementQuantityValue.java create mode 100644 dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml create mode 100644 dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml.post diff --git a/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java b/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java index 64773dc77..43259cd0f 100644 --- a/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java @@ -5,10 +5,12 @@ import java.util.stream.Stream; import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Reference; @@ -90,6 +92,9 @@ public Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemCom case DATETIME -> new DateTimeType("1900-01-01T00:00:00.000Z"); case URL -> new UriType("http://example.org/foo"); case REFERENCE -> new Reference("http://example.org/fhir/Placeholder/id"); + case CHOICE -> new Coding().setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); + case QUANTITY -> new Quantity().setValue(0).setUnit("unit") + .setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); default -> throw new RuntimeException("Type '" + question.getType().getDisplay() + "' in Questionnaire.item is not supported as answer type"); diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java index 7247732ab..4cf7b698b 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java @@ -14,6 +14,7 @@ import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Reference; @@ -95,6 +96,8 @@ public Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemCom case DATETIME -> new DateTimeType("1900-01-01T00:00:00.000Z"); case URL -> new UriType("http://example.org/foo"); case REFERENCE -> new Reference("http://example.org/fhir/Placeholder/id"); + case QUANTITY -> new Quantity().setValue(0).setUnit("unit") + .setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); default -> throw new RuntimeException("Type '" + question.getType().getDisplay() + "' in Questionnaire.item is not supported as answer type"); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ElementQuantityValue.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ElementQuantityValue.java new file mode 100644 index 000000000..9d430ebe7 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ElementQuantityValue.java @@ -0,0 +1,60 @@ +package dev.dsf.fhir.adapter; + +import java.math.BigDecimal; + +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Duration; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Quantity; + +public final class ElementQuantityValue +{ + public static ElementQuantityValue from(R element) + { + return new ElementQuantityValue(element.hasSystem() ? element.getSystem() : null, + element.hasCode() ? element.getCode() : null, element.hasUnit() ? element.getUnit() : null, + element.hasValue() ? element.getValue() : null, + element.hasComparator() ? element.getComparator() : null); + } + + private final String system; + private final String code; + private final String unit; + private final BigDecimal value; + private final Quantity.QuantityComparator comparator; + + private ElementQuantityValue(String system, String code, String unit, BigDecimal value, + Quantity.QuantityComparator comparator) + { + this.system = system; + this.code = code; + this.unit = unit; + this.value = value; + this.comparator = comparator; + } + + public String getSystem() + { + return system; + } + + public String getCode() + { + return code; + } + + public String getUnit() + { + return unit; + } + + public BigDecimal getValue() + { + return value; + } + + public String getComparator() + { + return comparator != null ? comparator.toCode() : null; + } +} diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java index 32f5ccbe2..73e95dfc2 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java @@ -4,16 +4,23 @@ import java.util.List; import java.util.function.BiConsumer; +import org.hl7.fhir.r4.model.Age; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Count; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.Distance; +import org.hl7.fhir.r4.model.Duration; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.MoneyQuantity; +import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.SimpleQuantity; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.TimeType; import org.hl7.fhir.r4.model.Type; @@ -39,7 +46,7 @@ private record Element(String questionnaire, String businessKey, String userTask } private record Item(boolean show, String id, String type, String label, String fhirType, String stringValue, - ElementSystemValue systemValueValue, Boolean booleanValue) + ElementSystemValue systemValueValue, Boolean booleanValue, ElementQuantityValue quantityValue) { } @@ -103,7 +110,7 @@ private Item toItem(QuestionnaireResponseItemComponent i) if (i.hasAnswer() && i.getAnswer().size() == 1) return toItem(show, linkId, text, i.getAnswerFirstRep().getValue()); else - return new Item(show, linkId, null, text, null, null, null, null); + return new Item(show, linkId, null, text, null, null, null, null, null); } private Item toItem(boolean show, String id, String label, Type typedValue) @@ -113,39 +120,42 @@ private Item toItem(boolean show, String id, String label, Type typedValue) return switch (typedValue) { case BooleanType b -> - new Item(show, id, "boolean", label, fhirType, null, null, b.hasValue() ? b.getValue() : null); + new Item(show, id, "boolean", label, fhirType, null, null, b.hasValue() ? b.getValue() : null, null); case DecimalType d -> new Item(show, id, "number", label, fhirType, - d.hasValue() ? String.valueOf(d.getValue()) : null, null, null); + d.hasValue() ? String.valueOf(d.getValue()) : null, null, null, null); case IntegerType i -> new Item(show, id, "number", label, fhirType, - i.hasValue() ? String.valueOf(i.getValue()) : null, null, null); + i.hasValue() ? String.valueOf(i.getValue()) : null, null, null, null); case DateType d -> new Item(show, id, "date", label, fhirType, - d.hasValue() ? format(d.getValue(), DATE_FORMAT) : null, null, null); + d.hasValue() ? format(d.getValue(), DATE_FORMAT) : null, null, null, null); case DateTimeType dt -> new Item(show, id, "datetime-local", label, fhirType, - dt.hasValue() ? format(dt.getValue(), DATE_TIME_FORMAT) : null, null, null); + dt.hasValue() ? format(dt.getValue(), DATE_TIME_FORMAT) : null, null, null, null); case TimeType t -> - new Item(show, id, "time", label, fhirType, t.hasValue() ? t.getValue() : null, null, null); + new Item(show, id, "time", label, fhirType, t.hasValue() ? t.getValue() : null, null, null, null); case StringType s -> - new Item(show, id, "text", label, fhirType, s.hasValue() ? s.getValue() : null, null, null); + new Item(show, id, "text", label, fhirType, s.hasValue() ? s.getValue() : null, null, null, null); case UriType u -> - new Item(show, id, "url", label, fhirType, u.hasValue() ? u.getValue() : null, null, null); + new Item(show, id, "url", label, fhirType, u.hasValue() ? u.getValue() : null, null, null, null); - case Coding c -> new Item(show, id, "coding", label, fhirType, null, ElementSystemValue.from(c), null); + case Coding c -> + new Item(show, id, "coding", label, fhirType, null, ElementSystemValue.from(c), null, null); case Reference r when r.hasReferenceElement() -> new Item(show, id, "url", label, fhirType + ".reference", - r.getReferenceElement().hasValue() ? r.getReferenceElement().getValue() : null, null, null); + r.getReferenceElement().hasValue() ? r.getReferenceElement().getValue() : null, null, null, null); case Reference r when r.hasIdentifier() -> new Item(show, id, "identifier", label, fhirType + ".identifier", - null, ElementSystemValue.from(r.getIdentifier()), null); + null, ElementSystemValue.from(r.getIdentifier()), null, null); + + case Quantity q -> + new Item(show, id, "quantity", label, fhirType, null, null, null, ElementQuantityValue.from(q)); // TODO case Attachment a -> - // TODO case Quantity q -> default -> { logger.warn("Element of type {}, not supported", fhirType); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceTask.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceTask.java index 97e24429f..20ec92075 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceTask.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceTask.java @@ -21,6 +21,7 @@ import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.InstantType; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.Task; @@ -56,17 +57,19 @@ private record Element(String process, String messageName, String businessKey, S } private record InputItem(String id, String type, String label, String labelTitle, String fhirType, - String stringValue, ElementSystemValue systemValueValue, Boolean booleanValue) + String stringValue, ElementSystemValue systemValueValue, Boolean booleanValue, + ElementQuantityValue quantityValue) { } private record OutputItem(String id, String type, String label, String labelTitle, String stringValue, - ElementSystemValue systemValueValue, Boolean booleanValue, List extension) + ElementSystemValue systemValueValue, Boolean booleanValue, ElementQuantityValue quantityValue, + List extension) { } private record ExtensionItem(String id, String type, String url, String stringValue, - ElementSystemValue systemValueValue, Boolean booleanValue) + ElementSystemValue systemValueValue, Boolean booleanValue, ElementQuantityValue quantityValue) { } @@ -177,11 +180,13 @@ private InputItem toItem(String id, String label, String labelTitle, Type typedV String stringValue = getStringValue(typedValue); ElementSystemValue systemValueValue = getSystemValueValue(typedValue); Boolean booleanValue = getBooleanValue(typedValue); + ElementQuantityValue quantityValue = getQuantityValue(typedValue); - if (stringValue == null && systemValueValue == null && booleanValue == null) - logger.warn("Output parameter with {} value, not supported", fhirType); + if (stringValue == null && systemValueValue == null && booleanValue == null && quantityValue == null) + logger.warn("Input parameter with {} value, not supported", fhirType); - return new InputItem(id, type, label, labelTitle, fhirType, stringValue, systemValueValue, booleanValue); + return new InputItem(id, type, label, labelTitle, fhirType, stringValue, systemValueValue, booleanValue, + quantityValue); } private OutputItem toOutputItem(TaskOutputComponent o) @@ -208,13 +213,14 @@ private OutputItem toOutputItem(String label, String labelTitle, Type typedValue String stringValue = getStringValue(typedValue); ElementSystemValue systemValueValue = getSystemValueValue(typedValue); Boolean booleanValue = getBooleanValue(typedValue); + ElementQuantityValue quantityValue = getQuantityValue(typedValue); - if (stringValue == null && systemValueValue == null && booleanValue == null) + if (stringValue == null && systemValueValue == null && booleanValue == null && quantityValue == null) logger.warn("Output parameter with {} value, not supported", typedValue.getClass().getAnnotation(DatatypeDef.class).name()); return new OutputItem(UUID.randomUUID().toString(), type, label, labelTitle, stringValue, systemValueValue, - booleanValue, extension); + booleanValue, quantityValue, extension); } private String getHtmlInputType(Type typedValue) @@ -232,6 +238,7 @@ private String getHtmlInputType(Type typedValue) case UriType _ -> "url"; case Coding _ -> "coding"; case Identifier _ -> "identifier"; + case Quantity _ -> "quantity"; case Reference r when r.hasReferenceElement() -> "url"; case Reference r when r.hasIdentifier() -> "identifier"; @@ -294,6 +301,14 @@ private Boolean getBooleanValue(Type typedValue) return null; } + private ElementQuantityValue getQuantityValue(Type typedValue) + { + if (typedValue instanceof Quantity q) + return ElementQuantityValue.from(q); + else + return null; + } + private List toExtensionItems(List extensions) { List items = new ArrayList<>(); @@ -310,10 +325,11 @@ private void addExtensionItem(String baseUrl, Extension extension, List addExtensionItem(url, e, items)); diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css index ea9a6a51c..5403cdb3e 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css @@ -1158,31 +1158,21 @@ div.row-text { background: var(--color-row-border-blue); } -.flex-element { +.flex-container { display: flex; flex-direction: row; } -.flex-element-100 { - width: 100%; - margin-right: 0em; -} - -.flex-element-67 { - width: 67%; -} - -.flex-element-50 { - width: 50%; - margin-right: 0em; +.flex-child { + flex: 1; } -.flex-element-33 { - width: 33%; +.flex-child-margin-right { + margin-right: 0.5em } -.flex-element-margin { - margin-right: 0.7em; +.comparator { + flex: 0; } .authorization { diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.css b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.css index 25d8eb123..7a8ea38e6 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.css +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.css @@ -214,7 +214,7 @@ input[type=number] { display: none; } -input.identifier-coding-code { +.identifier-coding-code { margin-top: 6px; } diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js index a202b3be0..64dd1c9fc 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js @@ -66,7 +66,6 @@ function readAndValidateTaskInput(input, row) { else return newTaskInputTyped(input.type, id, "value" + inputFhirType.charAt(0).toUpperCase() + inputFhirType.slice(1), htmlInputs[0].value, optional) } - else if (htmlInputs?.length === 2) { const input0FhirType = htmlInputs[0].getAttribute("fhir-type") const input1FhirType = htmlInputs[1].getAttribute("fhir-type") @@ -80,6 +79,17 @@ function readAndValidateTaskInput(input, row) { else if ("boolean.true" === input0FhirType && "boolean.false" == input1FhirType) return newTaskInputBoolean(input.type, id, htmlInputs[0].checked, htmlInputs[1].checked, optional) } + else if (htmlInputs?.length === 5) { + const input0FhirType = htmlInputs[0].getAttribute("fhir-type") + const input1FhirType = htmlInputs[1].getAttribute("fhir-type") + const input2FhirType = htmlInputs[2].getAttribute("fhir-type") + const input3FhirType = htmlInputs[3].getAttribute("fhir-type") + const input4FhirType = htmlInputs[4].getAttribute("fhir-type") + + if (input0FhirType.startsWith("Quantity")) { + return new newTaskInputQuantity(input.type, id, htmlInputs[0].value, htmlInputs[1].value, htmlInputs[2].value, htmlInputs[3].value, htmlInputs[4].value, optional) + } + } return { input: null, valid: false } } @@ -193,6 +203,27 @@ function newTaskInputBoolean(type, id, checkedTrue, checkedFalse, optional) { return { input: null, valid: optional } } +function newTaskInputQuantity(type, id, comparator, value, unit, system, code, optional) { + const result = validateQuantity(id, comparator, value, unit, system, code, optional, "Input") + + if (result.valid && result.value !== null) { + return { + input: { + type: type, + valueQuantity: { + comparator: result.value.comparator, + value: result.value.value, + unit: result.value.unit, + system: result.value.system, + code: result.value.code + } + }, + valid: true + } + } else + return { input: null, valid: result.valid } +} + function completeQuestionnaireResponse() { const questionnaireResponse = readQuestionnaireResponseAnswersFromForm() @@ -265,6 +296,17 @@ function readAndValidateQuestionnaireResponseItem(item, id) { else if ("boolean.true" === input0FhirType && "boolean.false" == input1FhirType) return newQuestionnaireResponseItemBoolean(item.text, id, htmlInputs[0].checked, htmlInputs[1].checked, optional) } + else if (htmlInputs?.length === 5) { + const input0FhirType = htmlInputs[0].getAttribute("fhir-type") + const input1FhirType = htmlInputs[1].getAttribute("fhir-type") + const input2FhirType = htmlInputs[2].getAttribute("fhir-type") + const input3FhirType = htmlInputs[3].getAttribute("fhir-type") + const input4FhirType = htmlInputs[4].getAttribute("fhir-type") + + if (input0FhirType.startsWith("Quantity")) { + return new newQuestionnaireResponseItemQuantity(item.text, id, htmlInputs[0].value, htmlInputs[1].value, htmlInputs[2].value, htmlInputs[3].value, htmlInputs[4].value, optional) + } + } return { item: null, valid: false } } @@ -373,6 +415,29 @@ function newQuestionnaireResponseItemBoolean(text, id, checkedTrue, checkedFalse } } +function newQuestionnaireResponseItemQuantity(text, id, comparator, value, unit, system, code, optional) { + const result = validateQuantity(id, comparator, value, unit, system, code, optional, "Item") + + if (result.valid && result.value !== null) { + const item = { + linkId: id, + text: text, + answer: [{ + valueQuantity: { + comparator: result.value.comparator, + value: result.value.value, + unit: result.value.unit, + system: result.value.system, + code: result.value.code + } + }] + } + + return { item: item, valid: true } + } else + return { input: null, valid: result.valid } +} + function validateAndConvert(id, fhirType, inputValue, optional, valueName) { const errorListElement = document.querySelector(`ul[for="${CSS.escape(id)}"]`) @@ -452,6 +517,44 @@ function validateIdentifier(id, system, value, optional, valueName) { } } +function validateQuantity(id, comparator, value, unit, system, code, optional, valueName) { + const comparatorEmpty = comparator === null || comparator.trim() === "" + const valueEmpty = value === null || value.trim() === "" + const unitEmpty = unit === null || unit.trim() === "" + const systemEmpty = system === null || system.trim() === "" + const codeEmpty = code === null || code.trim() === "" + + if (optional && valueEmpty && unitEmpty) + return { value: null, valid: true } + else { + const errorListElement = document.querySelector(`ul[for="${CSS.escape(id)}"]`) + + const resultComparator = validateStringInList(errorListElement, comparator, ["<", "<=", ">=", ">"], true, valueName + " comparator") + + const valueIsDecimal = !valueEmpty && (value.includes(".") || value.includes(",")) + const resultValue = valueIsDecimal ? validateDecimal(errorListElement, value, false, valueName + " value") : validateInteger(errorListElement, value, false, valueName + " value") + const resultUnit = validateString(errorListElement, unit, false, valueName + " unit") + + const resultSystem = validateUrl(errorListElement, system, (systemEmpty && codeEmpty), valueName + " system" + (!codeEmpty ? " (if code set)" : "")) + const resultCode = validateString(errorListElement, code, (systemEmpty && codeEmpty), valueName + " code (if system set)") + + if (resultComparator.valid && resultValue.valid && resultUnit.valid && resultSystem.valid && resultCode.valid) { + return { + value: { + comparator: resultComparator.value, + value: resultValue.value, + unit: resultUnit.value, + system: resultSystem.value, + code: resultCode.value + }, + valid: true + } + } + else + return { value: null, valid: false } + } +} + function validateType(errorListElement, value, optional, valueName, typeValid, typeSpecificError, toType) { const stringValid = value !== null && value.trim() !== "" @@ -475,6 +578,11 @@ function validateString(errorListElement, value, optional, valueName) { return validateType(errorListElement, value, optional, valueName, () => true, null, v => v) } +function validateStringInList(errorListElement, value, list, optional, valueName) { + const valueInList = s => list.includes(s) + return validateType(errorListElement, value, optional, valueName, valueInList, "not in [" + list.toString() + "]" , v => v) +} + function validateInteger(errorListElement, value, optional, valueName) { const integerValid = v => Number.isSafeInteger(parseInt(v)) && parseInt(v) == v diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceElements.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceElements.html index 85ca233da..c5bd23736 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceElements.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceElements.html @@ -32,6 +32,19 @@ +
+ + + [[${quantity.value}]] [[${quantity.unit}]] ([[${quantity.system}]] | [[${quantity.code}]]) + [[${quantity.value}]] [[${quantity.code}]] ([[${quantity.system}]]) + [[${quantity.value}]] [[${quantity.unit}]] ([[${quantity.system}]]) + [[${quantity.value}]] [[${quantity.unit}]] + [[${quantity.value}]] [[${quantity.code}]] + [[${quantity.value}]] [[${quantity.unit}]] + [[${quantity.value}]] + +
+
id diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html index 526ea5f88..ed3764605 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html @@ -67,6 +67,38 @@ Copy to Clipboard
+ + +
+
+ + Use placeholder value + Copy to Clipboard +
+
+ + Use placeholder value + Copy to Clipboard +
+
+ + Use placeholder value + Copy to Clipboard +
+
+
+
+ + Use placeholder value + Copy to Clipboard +
+
+ + Use placeholder value + Copy to Clipboard +
+
+
    diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceTask.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceTask.html index c3b52b4af..6a9e57397 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceTask.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceTask.html @@ -70,6 +70,38 @@

    Input

    Copy to Clipboard
    + + +
    +
    + + Insert Placeholder Value + Copy to Clipboard +
    +
    + + Insert Placeholder Value + Copy to Clipboard +
    +
    + + Insert Placeholder Value + Copy to Clipboard +
    +
    +
    +
    + + Insert Placeholder Value + Copy to Clipboard +
    +
    + + Insert Placeholder Value + Copy to Clipboard +
    +
    +
      @@ -124,6 +156,33 @@

      Output

      Copy to Clipboard
      + + +
      +
      + + Copy to Clipboard +
      +
      + + Copy to Clipboard +
      +
      + + Copy to Clipboard +
      +
      +
      +
      + + Copy to Clipboard +
      +
      + + Copy to Clipboard +
      +
      +
      @@ -166,6 +225,33 @@

      Output

      Copy to Clipboard
      + + +
      +
      + + Copy to Clipboard +
      +
      + + Copy to Clipboard +
      +
      + + Copy to Clipboard +
      +
      +
      +
      + + Copy to Clipboard +
      +
      + + Copy to Clipboard +
      +
      +
      diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml new file mode 100644 index 000000000..6af74121a --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml.post new file mode 100644 index 000000000..3325b13ff --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml.post @@ -0,0 +1 @@ +url=http://dsf.dev/fhir/StructureDefinition/questionnaire&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml index f95ca0b07..5a2b89525 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml @@ -244,6 +244,12 @@ + + + + + + From d9b84d40cd85b812c6a3cacff3f4e958b63b95aa Mon Sep 17 00:00:00 2001 From: Reto Wettstein Date: Fri, 31 Oct 2025 08:23:51 +0100 Subject: [PATCH 02/11] add comment why QuestionnaireResponse.item.answer.valueQuantity.comparator is currently not supported --- .../dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java | 3 +++ .../fhir/template/resourceQuestionnaireResponse.html | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java b/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java index 43259cd0f..530229b19 100644 --- a/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java @@ -95,6 +95,9 @@ public Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemCom case CHOICE -> new Coding().setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); case QUANTITY -> new Quantity().setValue(0).setUnit("unit") .setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); + // TODO: False positive validation error for QuestionnaireResponse.item.answer.valueQuantity.comparator, + // add comparator to Quantity as soon as https://github.com/hapifhir/org.hl7.fhir.core/issues/2224 is fixed + //.setComparator(Quantity.QuantityComparator.LESS_OR_EQUAL); default -> throw new RuntimeException("Type '" + question.getType().getDisplay() + "' in Questionnaire.item is not supported as answer type"); diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html index ed3764605..b49f19425 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html @@ -70,11 +70,14 @@
      + +
      Use placeholder value From eb4a6ff914cbb1ee51e9c7fb2f19edbb96809f0f Mon Sep 17 00:00:00 2001 From: Reto Wettstein Date: Fri, 31 Oct 2025 08:25:37 +0100 Subject: [PATCH 03/11] add missing whitespace --- .../dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java b/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java index 530229b19..7f6b4fb14 100644 --- a/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v1-impl/src/main/java/dev/dsf/bpe/v1/service/QuestionnaireResponseHelperImpl.java @@ -97,7 +97,7 @@ public Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemCom .setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); // TODO: False positive validation error for QuestionnaireResponse.item.answer.valueQuantity.comparator, // add comparator to Quantity as soon as https://github.com/hapifhir/org.hl7.fhir.core/issues/2224 is fixed - //.setComparator(Quantity.QuantityComparator.LESS_OR_EQUAL); + // .setComparator(Quantity.QuantityComparator.LESS_OR_EQUAL); default -> throw new RuntimeException("Type '" + question.getType().getDisplay() + "' in Questionnaire.item is not supported as answer type"); From f89f5bb2dc102d45b1c27820c7449c9b282c7008 Mon Sep 17 00:00:00 2001 From: Reto Wettstein Date: Fri, 31 Oct 2025 09:39:32 +0100 Subject: [PATCH 04/11] fix wrong order of div and th:block, fix questionnaire validation without comparator --- .../src/main/resources/fhir/static/form.js | 22 +++++-------------- .../resourceQuestionnaireResponse.html | 4 ++-- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js index ae7a175cd..437064b8a 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js @@ -81,10 +81,6 @@ function readAndValidateTaskInput(input, row) { } else if (htmlInputs?.length === 5) { const input0FhirType = htmlInputs[0].getAttribute("fhir-type") - const input1FhirType = htmlInputs[1].getAttribute("fhir-type") - const input2FhirType = htmlInputs[2].getAttribute("fhir-type") - const input3FhirType = htmlInputs[3].getAttribute("fhir-type") - const input4FhirType = htmlInputs[4].getAttribute("fhir-type") if (input0FhirType.startsWith("Quantity")) { return new newTaskInputQuantity(input.type, id, htmlInputs[0].value, htmlInputs[1].value, htmlInputs[2].value, htmlInputs[3].value, htmlInputs[4].value, optional) @@ -296,15 +292,14 @@ function readAndValidateQuestionnaireResponseItem(item, id) { else if ("boolean.true" === input0FhirType && "boolean.false" == input1FhirType) return newQuestionnaireResponseItemBoolean(item.text, id, htmlInputs[0].checked, htmlInputs[1].checked, optional) } - else if (htmlInputs?.length === 5) { + // TODO: False positive validation error for QuestionnaireResponse.item.answer.valueQuantity.comparator, + // adapt to === 5, remove "" and add htmlInputs[4].value before optional as soon as + // https://github.com/hapifhir/org.hl7.fhir.core/issues/2224 is fixed + else if (htmlInputs?.length === 4) { const input0FhirType = htmlInputs[0].getAttribute("fhir-type") - const input1FhirType = htmlInputs[1].getAttribute("fhir-type") - const input2FhirType = htmlInputs[2].getAttribute("fhir-type") - const input3FhirType = htmlInputs[3].getAttribute("fhir-type") - const input4FhirType = htmlInputs[4].getAttribute("fhir-type") if (input0FhirType.startsWith("Quantity")) { - return new newQuestionnaireResponseItemQuantity(item.text, id, htmlInputs[0].value, htmlInputs[1].value, htmlInputs[2].value, htmlInputs[3].value, htmlInputs[4].value, optional) + return new newQuestionnaireResponseItemQuantity(item.text, id, "", htmlInputs[0].value, htmlInputs[1].value, htmlInputs[2].value, htmlInputs[3].value, optional) } } @@ -417,7 +412,6 @@ function newQuestionnaireResponseItemBoolean(text, id, checkedTrue, checkedFalse function newQuestionnaireResponseItemQuantity(text, id, comparator, value, unit, system, code, optional) { const result = validateQuantity(id, comparator, value, unit, system, code, optional, "Item") - if (result.valid && result.value !== null) { const item = { linkId: id, @@ -432,7 +426,6 @@ function newQuestionnaireResponseItemQuantity(text, id, comparator, value, unit, } }] } - return { item: item, valid: true } } else return { input: null, valid: result.valid } @@ -585,19 +578,16 @@ function validateStringInList(errorListElement, value, list, optional, valueName function validateInteger(errorListElement, value, optional, valueName) { const integerValid = v => Number.isSafeInteger(parseInt(v)) && parseInt(v) == v - return validateType(errorListElement, value, optional, valueName, integerValid, "not an integer", parseInt) } function validateDecimal(errorListElement, value, optional, valueName) { const decimalValid = v => !isNaN(parseFloat(v)) && parseFloat(v) == v - return validateType(errorListElement, value, optional, valueName, decimalValid, "not a decimal", parseFloat) } function validateDate(errorListElement, value, optional, valueName) { const dateValid = v => !isNaN(new Date(v)) - return validateType(errorListElement, value, optional, valueName, dateValid, "is not a date", v => new Date(v).toISOString().substring(0, 10)) } @@ -614,13 +604,11 @@ function validateDateTime(errorListElement, value, optional, valueName) { // TODO precision YYYY, YYYY-MM, YYYY-MM-DD also valid const dateValid = v => !isNaN(new Date(v)) - return validateType(errorListElement, value, optional, valueName, dateValid, "is not a date-time", v => new Date(v).toISOString()) } function validateInstant(errorListElement, value, optional, valueName) { const dateValid = v => !isNaN(new Date(v)) - return validateType(errorListElement, value, optional, valueName, dateValid, "is not a date-time", v => new Date(v).toISOString()) } diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html index b49f19425..95bae0cc4 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html @@ -110,7 +110,7 @@
      -
      -
      + + \ No newline at end of file From af32fb4c5d09ef2687afd571086d46e7fe0aa6ac Mon Sep 17 00:00:00 2001 From: Reto Wettstein Date: Fri, 31 Oct 2025 09:56:20 +0100 Subject: [PATCH 05/11] add missing comment for QuestionnaireResponse.item.answer.valueQuantity.comparator --- .../dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java index 4cf7b698b..bd9e6a0a8 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java @@ -98,6 +98,9 @@ public Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemCom case REFERENCE -> new Reference("http://example.org/fhir/Placeholder/id"); case QUANTITY -> new Quantity().setValue(0).setUnit("unit") .setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); + // TODO: False positive validation error for QuestionnaireResponse.item.answer.valueQuantity.comparator, + // add comparator to Quantity as soon as https://github.com/hapifhir/org.hl7.fhir.core/issues/2224 is fixed + // .setComparator(Quantity.QuantityComparator.LESS_OR_EQUAL); default -> throw new RuntimeException("Type '" + question.getType().getDisplay() + "' in Questionnaire.item is not supported as answer type"); From 2e0513853e9cc9286c14a57b9072bb1b1b0fb2e6 Mon Sep 17 00:00:00 2001 From: Reto Wettstein Date: Fri, 31 Oct 2025 10:36:34 +0100 Subject: [PATCH 06/11] add tests for new supported answer types --- .../v1/service/QuestionnaireResponseTest.java | 12 ++++++---- .../profiles/QuestionnaireProfileTest.java | 24 +++++++++---------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/dsf-bpe/dsf-bpe-process-api-v1-impl/src/test/java/dev/dsf/bpe/v1/service/QuestionnaireResponseTest.java b/dsf-bpe/dsf-bpe-process-api-v1-impl/src/test/java/dev/dsf/bpe/v1/service/QuestionnaireResponseTest.java index ce8bcf89f..110e503a4 100644 --- a/dsf-bpe/dsf-bpe-process-api-v1-impl/src/test/java/dev/dsf/bpe/v1/service/QuestionnaireResponseTest.java +++ b/dsf-bpe/dsf-bpe-process-api-v1-impl/src/test/java/dev/dsf/bpe/v1/service/QuestionnaireResponseTest.java @@ -6,10 +6,12 @@ import java.util.List; import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Reference; @@ -175,7 +177,7 @@ public void testQuestionTypeReferenceToAnswerType() assertTrue(type instanceof Reference); } - @Test(expected = RuntimeException.class) + @Test public void testQuestionTypeChoiceToAnswerType() { QuestionnaireResponseHelper qrh = new QuestionnaireResponseHelperImpl("https://foo/fhir"); @@ -183,7 +185,8 @@ public void testQuestionTypeChoiceToAnswerType() Questionnaire.QuestionnaireItemComponent question = new Questionnaire.QuestionnaireItemComponent() .setType(Questionnaire.QuestionnaireItemType.CHOICE); - qrh.transformQuestionTypeToAnswerType(question); + Type type = qrh.transformQuestionTypeToAnswerType(question); + assertTrue(type instanceof Coding); } @Test(expected = RuntimeException.class) @@ -197,7 +200,7 @@ public void testQuestionTypeOpenChoiceToAnswerType() qrh.transformQuestionTypeToAnswerType(question); } - @Test(expected = RuntimeException.class) + @Test public void testQuestionTypeQuantityToAnswerType() { QuestionnaireResponseHelper qrh = new QuestionnaireResponseHelperImpl("https://foo/fhir"); @@ -205,7 +208,8 @@ public void testQuestionTypeQuantityToAnswerType() Questionnaire.QuestionnaireItemComponent question = new Questionnaire.QuestionnaireItemComponent() .setType(Questionnaire.QuestionnaireItemType.QUANTITY); - qrh.transformQuestionTypeToAnswerType(question); + Type type = qrh.transformQuestionTypeToAnswerType(question); + assertTrue(type instanceof Quantity); } @Test(expected = RuntimeException.class) diff --git a/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/QuestionnaireProfileTest.java b/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/QuestionnaireProfileTest.java index 3bf7d0086..ffa314efa 100644 --- a/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/QuestionnaireProfileTest.java +++ b/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/QuestionnaireProfileTest.java @@ -99,6 +99,18 @@ public void testQuestionnaireValidTypeDisplay() testQuestionnaireValidType(Questionnaire.QuestionnaireItemType.DISPLAY); } + @Test + public void testQuestionnaireValidTypeChoice() + { + testQuestionnaireValidType(Questionnaire.QuestionnaireItemType.CHOICE); + } + + @Test + public void testQuestionnaireInvalidTypeQuantity() + { + testQuestionnaireValidType(Questionnaire.QuestionnaireItemType.QUANTITY); + } + private void testQuestionnaireValidType(Questionnaire.QuestionnaireItemType type) { Questionnaire res = createQuestionnaire(type); @@ -122,12 +134,6 @@ public void testQuestionnaireInvalidTypeQuestion() testQuestionnaireInvalidType(Questionnaire.QuestionnaireItemType.QUESTION); } - @Test - public void testQuestionnaireInvalidTypeChoice() - { - testQuestionnaireInvalidType(Questionnaire.QuestionnaireItemType.CHOICE); - } - @Test public void testQuestionnaireInvalidTypeOpenChoice() { @@ -140,12 +146,6 @@ public void testQuestionnaireInvalidTypeAttachment() testQuestionnaireInvalidType(Questionnaire.QuestionnaireItemType.ATTACHMENT); } - @Test - public void testQuestionnaireInvalidTypeQuantity() - { - testQuestionnaireInvalidType(Questionnaire.QuestionnaireItemType.QUANTITY); - } - private void testQuestionnaireInvalidType(Questionnaire.QuestionnaireItemType type) { Questionnaire q = createQuestionnaire(Questionnaire.QuestionnaireItemType.STRING); From dc8906a1037aa1df55ff3143a30942ebad8d46aa Mon Sep 17 00:00:00 2001 From: Reto Wettstein Date: Fri, 31 Oct 2025 11:19:58 +0100 Subject: [PATCH 07/11] add choice option as well in v2 --- .../dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java index bd9e6a0a8..3a7b5bc61 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java @@ -96,6 +96,7 @@ public Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemCom case DATETIME -> new DateTimeType("1900-01-01T00:00:00.000Z"); case URL -> new UriType("http://example.org/foo"); case REFERENCE -> new Reference("http://example.org/fhir/Placeholder/id"); + case CHOICE -> new Coding().setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); case QUANTITY -> new Quantity().setValue(0).setUnit("unit") .setSystem("http://example.org/fhir/CodeSystem/name").setCode("code"); // TODO: False positive validation error for QuestionnaireResponse.item.answer.valueQuantity.comparator, From d2e3c4aa5f440bdddf2226e94cc982b99b9ba2dd Mon Sep 17 00:00:00 2001 From: Hauke Hund Date: Fri, 31 Oct 2025 18:52:45 +0100 Subject: [PATCH 08/11] formatting --- .../dsf-questionnaire-2.0.0.xml | 198 +++++++++--------- 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml index f43e43e5f..46953f62d 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-2.0.0.xml @@ -3,8 +3,8 @@ - - + + @@ -28,37 +28,37 @@
      - - + + - - + + - - + + - + - + - - + + - + - + - - - + + + + (type = 'quantity') " /> - - - - + + + + - - - - + + + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + \ No newline at end of file From d54555315748d5b2755761c9a399330ea8f4bcca Mon Sep 17 00:00:00 2001 From: Hauke Hund Date: Fri, 31 Oct 2025 18:56:29 +0100 Subject: [PATCH 09/11] removed unused imports --- .../java/dev/dsf/fhir/adapter/ElementQuantityValue.java | 3 --- .../dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java | 6 ------ 2 files changed, 9 deletions(-) diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ElementQuantityValue.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ElementQuantityValue.java index 9d430ebe7..097af7e10 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ElementQuantityValue.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ElementQuantityValue.java @@ -2,9 +2,6 @@ import java.math.BigDecimal; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Duration; -import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Quantity; public final class ElementQuantityValue diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java index 73e95dfc2..039540e44 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java @@ -4,23 +4,17 @@ import java.util.List; import java.util.function.BiConsumer; -import org.hl7.fhir.r4.model.Age; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Count; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; -import org.hl7.fhir.r4.model.Distance; -import org.hl7.fhir.r4.model.Duration; import org.hl7.fhir.r4.model.IntegerType; -import org.hl7.fhir.r4.model.MoneyQuantity; import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; import org.hl7.fhir.r4.model.Reference; -import org.hl7.fhir.r4.model.SimpleQuantity; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.TimeType; import org.hl7.fhir.r4.model.Type; From a6ebe39bde82cc6764394f5630d80fa48f02ea95 Mon Sep 17 00:00:00 2001 From: Hauke Hund Date: Sat, 1 Nov 2025 00:33:06 +0100 Subject: [PATCH 10/11] removed unused variable --- dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js | 1 - 1 file changed, 1 deletion(-) diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js index 437064b8a..e30f407cb 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js @@ -511,7 +511,6 @@ function validateIdentifier(id, system, value, optional, valueName) { } function validateQuantity(id, comparator, value, unit, system, code, optional, valueName) { - const comparatorEmpty = comparator === null || comparator.trim() === "" const valueEmpty = value === null || value.trim() === "" const unitEmpty = unit === null || unit.trim() === "" const systemEmpty = system === null || system.trim() === "" From 81367478c827f2e5d463c9fe901475f9a847dd73 Mon Sep 17 00:00:00 2001 From: Hauke Hund Date: Sat, 1 Nov 2025 01:04:46 +0100 Subject: [PATCH 11/11] added quantity and choice items --- .../test/service/QuestionnaireTestAnswer.java | 6 +++++ .../service/QuestionnaireTestAnswerCheck.java | 22 +++++++++++++++++++ .../resources/fhir/Questionnaire/test.xml | 12 ++++++++++ 3 files changed, 40 insertions(+) diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswer.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswer.java index b1e7239a9..abb7eee33 100644 --- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswer.java +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswer.java @@ -21,6 +21,7 @@ import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; @@ -192,6 +193,11 @@ else if ("identifiers".equals(type)) .setValue("External_Test_Organization"))); case "boolean-example" -> set(item, new BooleanType(true)); + + case "choice-example" -> + set(item, new Coding().setSystem("http://example.org/fhir/CodeSystem/name").setCode("code")); + + case "quantity-example" -> set(item, new Quantity().setValue(0).setUnit("unit")); } }); diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswerCheck.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswerCheck.java index 1adeb1bd5..a0478c66e 100644 --- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswerCheck.java +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswerCheck.java @@ -15,6 +15,7 @@ import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; @@ -22,6 +23,7 @@ import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.PrimitiveType; +import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; @@ -125,6 +127,11 @@ public void checkQuestionnaireResponse(ProcessPluginApi api) throws Exception .setValue("External_Test_Organization"))); case "boolean-example" -> test(item, new BooleanType(true)); + + case "choice-example" -> + test(item, new Coding().setSystem("http://example.org/fhir/CodeSystem/name").setCode("code")); + + case "quantity-example" -> test(item, new Quantity().setValue(0).setUnit("unit")); } }); @@ -159,6 +166,21 @@ private void test(QuestionnaireResponseItemComponent item, Type expected) expectTrue(r.getIdentifier().hasValue()); expectSame(((Reference) expected).getIdentifier().getSystem(), r.getIdentifier().getSystem()); expectSame(((Reference) expected).getIdentifier().getValue(), r.getIdentifier().getValue()); + + } + + case Coding c -> { + expectTrue(c.hasSystem()); + expectSame(((Coding) expected).getSystem(), c.getSystem()); + expectTrue(c.hasCode()); + expectSame(((Coding) expected).getCode(), c.getCode()); + } + + case Quantity q -> { + expectTrue(q.hasValue()); + expectSame(((Quantity) expected).getValue(), q.getValue()); + expectTrue(q.hasUnit()); + expectSame(((Quantity) expected).getUnit(), q.getUnit()); } default -> diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/fhir/Questionnaire/test.xml b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/fhir/Questionnaire/test.xml index 073c694af..68027dcde 100644 --- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/fhir/Questionnaire/test.xml +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/fhir/Questionnaire/test.xml @@ -91,4 +91,16 @@ + + + + + + + + + + + + \ No newline at end of file