Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix disable edit fields not working #3247

Merged
merged 2 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,31 @@
}
}

/**
* Set all questions that are not of type [Questionnaire.QuestionnaireItemType.GROUP] to readOnly if
* [readOnlyLinkIds] item are there while editing the form. This also generates the correct FHIRPath
* population expression for each question when mapped to the corresponding [QuestionnaireResponse]
*/
fun List<Questionnaire.QuestionnaireItemComponent>.prepareQuestionsForEditing(
path: String = "QuestionnaireResponse.item",
readOnlyLinkIds: List<String>? = emptyList(),

Check warning on line 234 in android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt#L232-L234

Added lines #L232 - L234 were not covered by tests
) {
forEach { item ->
if (item.type != Questionnaire.QuestionnaireItemType.GROUP) {
item.readOnly = readOnlyLinkIds?.contains(item.linkId) == true
item.item.prepareQuestionsForEditing(
"$path.where(linkId = '${item.linkId}').answer.item",
readOnlyLinkIds,
)
} else {
item.item.prepareQuestionsForEditing(
"$path.where(linkId = '${item.linkId}').item",
readOnlyLinkIds,

Check warning on line 246 in android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt#L244-L246

Added lines #L244 - L246 were not covered by tests
)
}
}
}

/** Delete resources in [QuestionnaireResponse.contained] from the database */
suspend fun QuestionnaireResponse.deleteRelatedResources(defaultRepository: DefaultRepository) {
contained.forEach { defaultRepository.delete(it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ abstract class RobolectricTest {
val latch = CountDownLatch(1)
val observer: Observer<T> =
object : Observer<T> {
override fun onChanged(o: T) {
data[0] = o
override fun onChanged(value: T) {
data[0] = value
latch.countDown()
liveData.removeObserver(this)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -895,4 +895,17 @@ class ResourceExtensionTest : RobolectricTest() {
patient.appendOrganizationInfo(listOf("Organization/12345"))
Assert.assertEquals("Organization/12345", patient.managingOrganization.reference)
}

@Test
fun `prepareQuestionsForEditing should set readOnly correctly when readOnlyLinkIds passed`() {
val questionnaire = Questionnaire()
questionnaire.item.add(Questionnaire.QuestionnaireItemComponent().apply { linkId = "1" })
questionnaire.item.add(Questionnaire.QuestionnaireItemComponent().apply { linkId = "2" })
questionnaire.item.add(Questionnaire.QuestionnaireItemComponent().apply { linkId = "3" })
questionnaire.item.prepareQuestionsForEditing("", readOnlyLinkIds = listOf("1", "3"))

Assert.assertTrue(questionnaire.item[0].readOnly)
Assert.assertFalse(questionnaire.item[1].readOnly)
Assert.assertTrue(questionnaire.item[2].readOnly)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.hl7.fhir.r4.model.Parameters
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.QuestionnaireResponse
import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent
import org.hl7.fhir.r4.model.RelatedPerson
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
Expand Down Expand Up @@ -90,6 +91,7 @@
import org.smartregister.fhircore.engine.util.extension.generateMissingId
import org.smartregister.fhircore.engine.util.extension.isIn
import org.smartregister.fhircore.engine.util.extension.prePopulateInitialValues
import org.smartregister.fhircore.engine.util.extension.prepareQuestionsForEditing
import org.smartregister.fhircore.engine.util.extension.prepareQuestionsForReadingOrEditing
import org.smartregister.fhircore.engine.util.extension.showToast
import org.smartregister.fhircore.engine.util.extension.updateLastUpdated
Expand Down Expand Up @@ -167,6 +169,10 @@
)
}

if (questionnaireConfig.isEditable()) {
item.prepareQuestionsForEditing(readOnlyLinkIds = questionnaireConfig.readOnlyLinkIds)

Check warning on line 173 in android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt

View check run for this annotation

Codecov / codecov/patch

android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt#L173

Added line #L173 was not covered by tests
}

// Pre-populate questionnaire items with configured values
allActionParameters
?.filter { (it.paramType == ActionParameterType.PREPOPULATE && it.value.isNotEmpty()) }
Expand Down Expand Up @@ -974,7 +980,7 @@
)
?.let {
QuestionnaireResponse().apply {
item = it.item
item = it.item.removeUnAnsweredItems()

Check warning on line 983 in android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt

View check run for this annotation

Codecov / codecov/patch

android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt#L983

Added line #L983 was not covered by tests
// Clearing the text prompts the SDK to re-process the content, which includes HTML
clearText()
}
Expand All @@ -986,6 +992,13 @@
return Pair(questionnaireResponse, launchContextResources)
}

private fun List<QuestionnaireResponseItemComponent>.removeUnAnsweredItems():
List<QuestionnaireResponseItemComponent> {
return this.filter { it.hasAnswer() || it.item.isNotEmpty() }
.onEach { it.item = it.item.removeUnAnsweredItems() }

Check warning on line 998 in android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt

View check run for this annotation

Codecov / codecov/patch

android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt#L998

Added line #L998 was not covered by tests
.filter { it.hasAnswer() || it.item.isNotEmpty() }
}

/**
* Return [Resource]s to be used in the launch context of the questionnaire. Launch context allows
* information to be passed into questionnaire based on the context in which the questionnaire is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import android.app.Application
import androidx.test.core.app.ApplicationProvider
import ca.uhn.fhir.parser.IParser
import com.google.android.fhir.FhirEngine
import com.google.android.fhir.SearchResult
import com.google.android.fhir.datacapture.mapping.ResourceMapper
import com.google.android.fhir.db.ResourceNotFoundException
import com.google.android.fhir.get
import com.google.android.fhir.logicalId
import com.google.android.fhir.search.Search
import com.google.android.fhir.workflow.FhirOperator
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
Expand Down Expand Up @@ -83,6 +85,7 @@ import org.smartregister.fhircore.engine.configuration.app.ConfigService
import org.smartregister.fhircore.engine.data.local.DefaultRepository
import org.smartregister.fhircore.engine.domain.model.ActionParameter
import org.smartregister.fhircore.engine.domain.model.ActionParameterType
import org.smartregister.fhircore.engine.domain.model.QuestionnaireType
import org.smartregister.fhircore.engine.domain.model.RuleConfig
import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor
import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor
Expand Down Expand Up @@ -1264,37 +1267,136 @@ class QuestionnaireViewModelTest : RobolectricTest() {
}

@Test
fun testThatPopulateQuestionnaireSetInitialDefaultValueForQuestionnaire() = runTest {
val questionnaireWithDefaultDate =
fun testThatPopulateQuestionnaireSetInitialDefaultValueForQuestionnaireInitialExpression() =
runTest {
val questionnaireWithDefaultDate =
Questionnaire().apply {
id = questionnaireConfig.id
addItem(
Questionnaire.QuestionnaireItemComponent().apply {
linkId = "defaultedDate"
type = Questionnaire.QuestionnaireItemType.DATE
addExtension(
Extension(
"http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
Expression().apply {
language = "text/fhirpath"
expression = "today()"
},
),
)
},
)
}
coEvery { fhirEngine.get(ResourceType.Questionnaire, questionnaireConfig.id) } returns
questionnaireWithDefaultDate

questionnaireViewModel.populateQuestionnaire(
questionnaireWithDefaultDate,
questionnaireConfig,
emptyList(),
)
val initialValueDate =
questionnaireWithDefaultDate.item
.first { it.linkId == "defaultedDate" }
.initial
.first()
.value as DateType
Assert.assertTrue(initialValueDate.isToday)
}

@Test
fun testThatPopulateQuestionnaireReturnsQuestionnaireResponseWithUnAnsweredRemoved() = runTest {
val questionnaireConfig1 =
questionnaireConfig.copy(
resourceType = ResourceType.Patient,
resourceIdentifier = patient.logicalId,
type = QuestionnaireType.EDIT.name,
)

val questionnaireWithInitialValue =
Questionnaire().apply {
id = questionnaireConfig.id
id = questionnaireConfig1.id
addItem(
Questionnaire.QuestionnaireItemComponent().apply {
linkId = "defaultedDate"
type = Questionnaire.QuestionnaireItemType.DATE
addExtension(
Extension(
"http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
Expression().apply {
language = "text/fhirpath"
expression = "today()"
},
),
linkId = "group-1"
type = Questionnaire.QuestionnaireItemType.GROUP
addItem(
Questionnaire.QuestionnaireItemComponent().apply {
linkId = "linkid-1"
type = Questionnaire.QuestionnaireItemType.STRING
addInitial(Questionnaire.QuestionnaireItemInitialComponent(StringType("---")))
},
)

addItem(
Questionnaire.QuestionnaireItemComponent().apply {
linkId = "linkid-1.1"
type = Questionnaire.QuestionnaireItemType.STRING
addInitial(Questionnaire.QuestionnaireItemInitialComponent(StringType("---")))
},
)
},
)

addItem(
Questionnaire.QuestionnaireItemComponent().apply {
linkId = "linkid-2"
type = Questionnaire.QuestionnaireItemType.STRING
},
)
}
coEvery { fhirEngine.get(ResourceType.Questionnaire, questionnaireConfig.id) } returns
questionnaireWithDefaultDate
val questionnaireResponse =
QuestionnaireResponse().apply {
addItem(
QuestionnaireResponse.QuestionnaireResponseItemComponent().apply {
linkId = "group-1"
addItem(
QuestionnaireResponse.QuestionnaireResponseItemComponent().apply {
linkId = "linkid-1"
},
)
addItem(
QuestionnaireResponse.QuestionnaireResponseItemComponent().apply {
linkId = "linkid-1.1"
addAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = StringType("World")
},
)
},
)
},
)

questionnaireViewModel.populateQuestionnaire(
questionnaireWithDefaultDate,
questionnaireConfig,
emptyList(),
)
val initialValueDate =
questionnaireWithDefaultDate.item.first { it.linkId == "defaultedDate" }.initial.first().value
as DateType
Assert.assertTrue(initialValueDate.isToday)
addItem(
QuestionnaireResponse.QuestionnaireResponseItemComponent().apply {
linkId = "linkid-2"
addAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = StringType("45678")
},
)
},
)
}
coEvery {
fhirEngine.get(questionnaireConfig1.resourceType!!, questionnaireConfig1.resourceIdentifier!!)
} returns patient

coEvery { fhirEngine.search<QuestionnaireResponse>(any<Search>()) } returns
listOf(
SearchResult(questionnaireResponse, included = null, revIncluded = null),
)

Assert.assertNotNull(questionnaireResponse.find("linkid-1"))
val result =
questionnaireViewModel.populateQuestionnaire(
questionnaireWithInitialValue,
questionnaireConfig1,
emptyList(),
)
Assert.assertNotNull(result.first)
Assert.assertTrue(result.first!!.find("linkid-1") == null)
}
}
Loading