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

Support Stringtype, Integertype as answer option type when item type is choice. #1399

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,34 @@ package com.google.android.fhir.datacapture

import com.google.android.fhir.getLocalizedText
import org.hl7.fhir.r4.model.BooleanType
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.DateType
import org.hl7.fhir.r4.model.IntegerType
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.StringType
import org.hl7.fhir.r4.model.TimeType

internal const val EXTENSION_OPTION_EXCLUSIVE_URL =
"http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive"

val Questionnaire.QuestionnaireItemAnswerOptionComponent.displayString: String
/**
* Text value for answer option [Questionnaire.QuestionnaireItemAnswerOptionComponent] if answer
* option is [IntegerType] or [StringType] or [Coding] type.
*/
internal val Questionnaire.QuestionnaireItemAnswerOptionComponent.displayString: String
get() {
if (!hasValueCoding()) {
throw IllegalArgumentException("Answer option does not having coding.")
}
val display = valueCoding.displayElement.getLocalizedText() ?: valueCoding.display
return if (display.isNullOrEmpty()) {
valueCoding.code
} else {
display
return when (value) {
is IntegerType, is DateType, is TimeType -> value.primitiveValue()
is StringType -> (value as StringType).getLocalizedText() ?: value.toString()
is Coding -> {
val display = valueCoding.displayElement.getLocalizedText() ?: valueCoding.display
if (display.isNullOrEmpty()) {
valueCoding.code
} else {
display
}
}
else -> throw IllegalArgumentException("$value is not supported.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test this

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To test else part, it is required to set value of QuestionnaireItemAnswerOptionComponent with not supported type, if you do so then fhir library api throws error.
throw new Error("Not the right type for Questionnaire.item.answerOption.value[x]: "+value.fhirType());

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok so the QuestionnaireItemAnswerOptionComponent class doesn't allow you to set a type that's not one of the types here. so it's sort of non-strict type safety. got it.

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ internal class QuestionnaireItemAdapter(
}
}

if (questionnaireItemViewItem.answerOption.isNotEmpty()) {
return getChoiceViewHolderType(questionnaireItemViewItem).value
}

return when (val type = questionnaireItem.type) {
santosh-pingle marked this conversation as resolved.
Show resolved Hide resolved
QuestionnaireItemType.GROUP -> QuestionnaireItemViewHolderType.GROUP
QuestionnaireItemType.BOOLEAN -> QuestionnaireItemViewHolderType.BOOLEAN_TYPE_PICKER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import com.google.android.fhir.datacapture.ChoiceOrientationTypes
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.choiceOrientation
import com.google.android.fhir.datacapture.displayString
import com.google.android.fhir.datacapture.optionExclusive
import com.google.android.fhir.datacapture.validation.ValidationResult
import com.google.android.fhir.datacapture.validation.getSingleStringValidationMessage
Expand Down Expand Up @@ -101,7 +102,7 @@ internal object QuestionnaireItemCheckBoxGroupViewHolderFactory :
val checkbox =
checkboxLayout.findViewById<CheckBox>(R.id.check_box).apply {
id = viewId
text = answerOption.valueCoding.display
text = answerOption.displayString
isChecked = questionnaireItemViewItem.isAnswerOptionSelected(answerOption)
layoutParams =
ViewGroup.LayoutParams(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ internal object QuestionnaireItemDropDownViewHolderFactory :
} else {
questionnaireItemViewItem.singleAnswerOrNull =
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent()
.setValue(questionnaireItemViewItem.answerOption[position - 1].valueCoding)
.setValue(questionnaireItemViewItem.answerOption[position - 1].value)
}
onAnswerChanged(autoCompleteTextView.context)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ import kotlin.test.assertFailsWith
import kotlinx.coroutines.runBlocking
import org.hl7.fhir.r4.model.BooleanType
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.DateType
import org.hl7.fhir.r4.model.Extension
import org.hl7.fhir.r4.model.IntegerType
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.StringType
import org.hl7.fhir.r4.model.TimeType
import org.hl7.fhir.r4.utils.ToolingExtensions
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -60,6 +63,23 @@ class MoreAnswerOptionsTest {
assertFailsWith<IllegalArgumentException> { answerOption.displayString }
}

@Test
fun getDisplayString_integerType_shouldReturnIntegerValue() {
val answerOption =
Questionnaire.QuestionnaireItemAnswerOptionComponent().setValue(IntegerType().setValue(1))

assertThat(answerOption.displayString).isEqualTo("1")
}

@Test
fun getDisplayString_stringType_shouldReturnStringValue() {
val answerOption =
Questionnaire.QuestionnaireItemAnswerOptionComponent()
.setValue(StringType().setValue("string type value"))

assertThat(answerOption.displayString).isEqualTo("string type value")
}

@Test
fun getDisplayString_validExtension_shouldReturnLocalizedText() {
val answerOption =
Expand Down Expand Up @@ -105,6 +125,42 @@ class MoreAnswerOptionsTest {
assertThat(answerOption.displayString).isEqualTo("Test Code")
}

@Test
fun getDisplayString_stringType_validTranslationExtension_shouldReturnLocalizedText() {
val answerOption =
Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value =
StringType().apply {
value = "string value"
addExtension(
Extension(ToolingExtensions.EXT_TRANSLATION).apply {
addExtension(Extension("lang", StringType("vi-VN")))
addExtension(Extension("content", StringType("Thí nghiệm")))
}
)
}
}
Locale.setDefault(Locale.forLanguageTag("vi-VN"))

assertThat(answerOption.displayString).isEqualTo("Thí nghiệm")
}

@Test
fun getDisplayString_timeType_shouldReturnTimeValue() {
val answerOption =
Questionnaire.QuestionnaireItemAnswerOptionComponent().setValue(TimeType("16:25:00"))

assertThat(answerOption.displayString).isEqualTo("16:25:00")
}

@Test
fun getDisplayString_dateType_shouldReturnDateValue() {
val answerOption =
Questionnaire.QuestionnaireItemAnswerOptionComponent().setValue(DateType("2022-06-23"))

assertThat(answerOption.displayString).isEqualTo("2022-06-23")
}

@Test
fun optionExclusiveExtension_valueTrue_returnsTrue() = runBlocking {
val answerOptionTest = Coding("test", "option", "1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ import com.google.android.fhir.datacapture.views.QuestionnaireItemViewItem
import com.google.common.truth.Truth.assertThat
import org.hl7.fhir.r4.model.CodeableConcept
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.DateType
import org.hl7.fhir.r4.model.Extension
import org.hl7.fhir.r4.model.IntegerType
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.QuestionnaireResponse
import org.hl7.fhir.r4.model.StringType
import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -88,6 +91,28 @@ class QuestionnaireItemAdapterTest {
.isEqualTo(QuestionnaireItemViewHolderType.DATE_PICKER.value)
}

@Test
fun getItemViewType_dateItemType_answerOption_shouldReturnDropDownViewHolderType() {
val questionnaireItemAdapter = QuestionnaireItemAdapter()
val questionnaireItemComponent =
Questionnaire.QuestionnaireItemComponent().apply {
type = Questionnaire.QuestionnaireItemType.DATE
answerOption =
listOf(Questionnaire.QuestionnaireItemAnswerOptionComponent(DateType("2022-06-22")))
}
questionnaireItemAdapter.submitList(
listOf(
QuestionnaireItemViewItem(
questionnaireItemComponent,
QuestionnaireResponse.QuestionnaireResponseItemComponent()
) {}
)
)

assertThat(questionnaireItemAdapter.getItemViewType(0))
.isEqualTo(QuestionnaireItemViewHolderType.RADIO_GROUP.value)
}

@Test
fun getItemViewType_dateTimeItemType_shouldReturnDateTimePickerViewHolderType() {
val questionnaireItemAdapter = QuestionnaireItemAdapter()
Expand Down Expand Up @@ -152,6 +177,28 @@ class QuestionnaireItemAdapterTest {
.isEqualTo(QuestionnaireItemViewHolderType.PHONE_NUMBER.value)
}

@Test
fun getItemViewType_stringItemType_answerOption_shouldReturnDropDownViewHolderType() {
val questionnaireItemAdapter = QuestionnaireItemAdapter()
val questionnaireItemComponent =
Questionnaire.QuestionnaireItemComponent().apply {
type = Questionnaire.QuestionnaireItemType.STRING
answerOption =
listOf(Questionnaire.QuestionnaireItemAnswerOptionComponent(StringType("option-1")))
}
questionnaireItemAdapter.submitList(
listOf(
QuestionnaireItemViewItem(
questionnaireItemComponent,
QuestionnaireResponse.QuestionnaireResponseItemComponent()
) {}
)
)

assertThat(questionnaireItemAdapter.getItemViewType(0))
.isEqualTo(QuestionnaireItemViewHolderType.RADIO_GROUP.value)
}

@Test
fun getItemViewType_textItemType_shouldReturnEditTextViewHolderType() {
val questionnaireItemAdapter = QuestionnaireItemAdapter()
Expand Down Expand Up @@ -218,6 +265,28 @@ class QuestionnaireItemAdapterTest {
.isEqualTo(QuestionnaireItemViewHolderType.SLIDER.value)
}

@Test
fun getItemViewType_integerItemType_answerOption_shouldReturnDropDownViewHolderType() {
val questionnaireItemAdapter = QuestionnaireItemAdapter()
val questionnaireItemComponent =
Questionnaire.QuestionnaireItemComponent().apply {
type = Questionnaire.QuestionnaireItemType.INTEGER
answerOption =
listOf(Questionnaire.QuestionnaireItemAnswerOptionComponent(IntegerType("1")))
}
questionnaireItemAdapter.submitList(
listOf(
QuestionnaireItemViewItem(
questionnaireItemComponent,
QuestionnaireResponse.QuestionnaireResponseItemComponent()
) {}
)
)

assertThat(questionnaireItemAdapter.getItemViewType(0))
.isEqualTo(QuestionnaireItemViewHolderType.RADIO_GROUP.value)
}

@Test
fun getItemViewType_decimalItemType_shouldReturnEditTextDecimalViewHolderType() {
val questionnaireItemAdapter = QuestionnaireItemAdapter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.displayString
import com.google.android.material.textfield.TextInputLayout
import com.google.common.truth.Truth.assertThat
import kotlin.test.assertFailsWith
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.QuestionnaireResponse
import org.hl7.fhir.r4.model.StringType
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
Expand Down Expand Up @@ -163,27 +161,6 @@ class QuestionnaireItemDropDownViewHolderFactoryTest {
.isEqualTo(answerOption.displayString)
}

@Test
fun shouldThrowErrorForAnswerOptionWithoutCoding() {
val answerOption =
Questionnaire.QuestionnaireItemAnswerOptionComponent().apply { value = StringType("test") }

assertFailsWith<IllegalArgumentException> {
viewHolder.bind(
QuestionnaireItemViewItem(
Questionnaire.QuestionnaireItemComponent().apply { addAnswerOption(answerOption) },
QuestionnaireResponse.QuestionnaireResponseItemComponent().apply {
addAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = answerOption.value
}
)
}
) {}
)
}
}

@Test
fun displayValidationResult_error_shouldShowErrorMessage() {
viewHolder.bind(
Expand Down