diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireResponseExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireResponseExtension.kt index 7f606a66e6..0e26849dbd 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireResponseExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireResponseExtension.kt @@ -33,3 +33,31 @@ private fun List.clearText() { } } } + +/** Borrows from: https://github.com/google/android-fhir/pull/1936 */ +fun QuestionnaireResponse.packRepeatedGroups() { + item = item.packRepeatedGroups() +} + +private fun List.packRepeatedGroups(): + List { + forEach { it -> + it.item = it.item.packRepeatedGroups() + it.answer.forEach { it.item = it.item.packRepeatedGroups() } + } + val linkIdToPackedResponseItems = + groupBy { it.linkId } + .mapValues { (linkId, questionnaireResponseItems) -> + questionnaireResponseItems.singleOrNull() + ?: QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { + this.linkId = linkId + answer = + questionnaireResponseItems.map { + QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { + item = it.item + } + } + } + } + return map { it.linkId }.distinct().map { linkIdToPackedResponseItems[it]!! } +} diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 391edaa178..f94cb2b998 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -90,6 +90,7 @@ import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.extension.find import org.smartregister.fhircore.engine.util.extension.generateMissingId import org.smartregister.fhircore.engine.util.extension.isIn +import org.smartregister.fhircore.engine.util.extension.packRepeatedGroups import org.smartregister.fhircore.engine.util.extension.prePopulateInitialValues import org.smartregister.fhircore.engine.util.extension.prepareQuestionsForEditing import org.smartregister.fhircore.engine.util.extension.prepareQuestionsForReadingOrEditing @@ -675,7 +676,10 @@ constructor( return QuestionnaireResponseValidator.validateQuestionnaireResponse( questionnaire = Questionnaire().apply { item = validQuestionnaireItems }, questionnaireResponse = - QuestionnaireResponse().apply { item = validQuestionnaireResponseItems }, + QuestionnaireResponse().apply { + item = validQuestionnaireResponseItems + packRepeatedGroups() + }, context = context, ) .values diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 4f24ee3175..ca422badd5 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -706,6 +706,226 @@ class QuestionnaireViewModelTest : RobolectricTest() { } } + @Test + fun testValidateQuestionnaireResponseWithRepeatsGroup() = runTest { + val questionnaireString = + """ + { + "resourceType": "Questionnaire", + "item": [ + { + "linkId": "systolic-bp", + "type": "integer", + "required": true + }, + { + "linkId": "blood-pressure-repeating-group", + "type": "group", + "required": false, + "repeats": true, + "item": [ + { + "linkId": "systolic-bp", + "type": "integer", + "required": true + } + ] + } + ] + } + """ + .trimIndent() + val questionnaireResponseString = + """ + { + "resourceType": "QuestionnaireResponse", + "item": [ + { + "linkId": "systolic-bp", + "answer": [ + { + "valueInteger": 123 + } + ] + }, + { + "linkId": "blood-pressure-repeating-group", + "item": [ + { + "linkId": "systolic-bp", + "answer": [ + { + "valueInteger": 124 + } + ] + } + ] + }, + { + "linkId": "blood-pressure-repeating-group", + "item": [ + { + "linkId": "systolic-bp", + "answer": [ + { + "valueInteger": 125 + } + ] + } + ] + }, + { + "linkId": "blood-pressure-repeating-group", + "item": [ + { + "linkId": "systolic-bp", + "answer": [ + { + "valueInteger": 126 + } + ] + } + ] + } + ] + } + """ + .trimIndent() + val questionnaire = parser.parseResource(questionnaireString) as Questionnaire + val questionnaireResponse = + parser.parseResource(questionnaireResponseString) as QuestionnaireResponse + val result = + questionnaireViewModel.validateQuestionnaireResponse( + questionnaire, + questionnaireResponse, + context, + ) + Assert.assertTrue(result) + } + + @Test + fun testValidateQuestionnaireResponseWithNestedRepeatsGroup() = runTest { + val questionnaireString = + """ + { + "resourceType": "Questionnaire", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/questionnaire-item-control", + "code": "page", + "display": "Page 1" + } + ], + "text": "Page 1" + } + } + ], + "linkId": "page-1", + "type": "group", + "item": [ + { + "linkId": "systolic-bp", + "type": "integer", + "required": true + }, + { + "linkId": "blood-pressure-repeating-group", + "type": "group", + "required": false, + "repeats": true, + "item": [ + { + "linkId": "systolic-bp", + "type": "integer", + "required": true + } + ] + } + ] + } + ] + } + """ + .trimIndent() + val questionnaireResponseString = + """ + { + "resourceType": "QuestionnaireResponse", + "item": [ + { + "linkId": "page-1", + "item": [ + { + "linkId": "systolic-bp", + "answer": [ + { + "valueInteger": 123 + } + ] + }, + { + "linkId": "blood-pressure-repeating-group", + "item": [ + { + "linkId": "systolic-bp", + "answer": [ + { + "valueInteger": 124 + } + ] + } + ] + }, + { + "linkId": "blood-pressure-repeating-group", + "item": [ + { + "linkId": "systolic-bp", + "answer": [ + { + "valueInteger": 125 + } + ] + } + ] + }, + { + "linkId": "blood-pressure-repeating-group", + "item": [ + { + "linkId": "systolic-bp", + "answer": [ + { + "valueInteger": 126 + } + ] + } + ] + } + ] + } + ] + } + """ + .trimIndent() + val questionnaire = parser.parseResource(questionnaireString) as Questionnaire + val questionnaireResponse = + parser.parseResource(questionnaireResponseString) as QuestionnaireResponse + val result = + questionnaireViewModel.validateQuestionnaireResponse( + questionnaire, + questionnaireResponse, + context, + ) + Assert.assertTrue(result) + } + @Test fun testExecuteCqlShouldInvokeRunCqlLibrary() = runTest { val bundle =