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

Implement inequality operators (<, >, <=, >=) in the enable when statement #848

Merged
merged 20 commits into from
Nov 25, 2021
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
1 change: 1 addition & 0 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
api(Dependencies.HapiFhir.structuresR4)

implementation(Dependencies.fhirUcum)
implementation(Dependencies.Kotlin.kotlinTestJunit)

testImplementation(Dependencies.AndroidxTest.core)
testImplementation(Dependencies.junit)
Expand Down
54 changes: 32 additions & 22 deletions common/src/main/java/com/google/android/fhir/MoreTypes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.google.android.fhir
import java.util.Calendar
import java.util.Date
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.Quantity
import org.hl7.fhir.r4.model.Type

/**
Expand All @@ -41,30 +42,39 @@ fun equals(a: Type, b: Type): Boolean {
throw NotImplementedError("Comparison for type ${a::class.java} not supported.")
}

operator fun Type.compareTo(value: Type?): Int {
if (value != null) {
if (!this.fhirType().equals(value.fhirType())) {
throw IllegalArgumentException(
"Cannot compare different data types: ${this.fhirType()} and ${value.fhirType()}"
)
operator fun Type.compareTo(value: Type): Int {
if (!this.fhirType().equals(value.fhirType())) {
throw IllegalArgumentException(
"Cannot compare different data types: ${this.fhirType()} and ${value.fhirType()}"
)
}
when {
this.fhirType().equals("integer") -> {
return this.primitiveValue().toInt().compareTo(value.primitiveValue().toInt())
}
when {
this.fhirType().equals("integer") -> {
return this.primitiveValue().toInt().compareTo(value.primitiveValue().toInt())
}
this.fhirType().equals("decimal") -> {
return this.primitiveValue().toBigDecimal().compareTo(value.primitiveValue().toBigDecimal())
}
this.fhirType().equals("date") -> {
return clearTimeFromDateValue(this.dateTimeValue().value)
.compareTo(clearTimeFromDateValue(value.dateTimeValue().value))
}
this.fhirType().equals("dateTime") -> {
return this.dateTimeValue().value.compareTo(value.dateTimeValue().value)
}
else -> {
throw NotImplementedError()
this.fhirType().equals("decimal") -> {
return this.primitiveValue().toBigDecimal().compareTo(value.primitiveValue().toBigDecimal())
}
this.fhirType().equals("date") -> {
return clearTimeFromDateValue(this.dateTimeValue().value)
.compareTo(clearTimeFromDateValue(value.dateTimeValue().value))
}
this.fhirType().equals("dateTime") -> {
return this.dateTimeValue().value.compareTo(value.dateTimeValue().value)
}
this.fhirType().equals("Quantity") -> {
val quantity = UnitConverter.getCanonicalForm(UcumValue((this as Quantity).code, this.value))
val anotherQuantity =
UnitConverter.getCanonicalForm(UcumValue((value as Quantity).code, value.value))
if (quantity.code != anotherQuantity.code) {
throw IllegalArgumentException(
"Cannot compare different quantity codes: ${quantity.code} and ${anotherQuantity.code}"
)
}
return quantity.value.compareTo(anotherQuantity.value)
}
else -> {
throw NotImplementedError()
}
}
return 0
Expand Down
34 changes: 34 additions & 0 deletions common/src/test/java/com/google/android/fhir/MoreTypesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.google.android.fhir
import android.os.Build
import com.google.common.truth.Truth.assertThat
import java.util.Calendar
import kotlin.test.assertFailsWith
import org.hl7.fhir.r4.model.Attachment
import org.hl7.fhir.r4.model.BooleanType
import org.hl7.fhir.r4.model.Coding
Expand Down Expand Up @@ -204,4 +205,37 @@ class MoreTypesTest {
assertThat(exception.message)
.isEqualTo("Cannot compare different data types: decimal and integer")
}

@Test
fun compareTo_quantityType_shouldReturnPositiveValue() {
val value = Quantity().setCode("h").setValue(10)
val otherValue = Quantity().setCode("h").setValue(5)

assertThat(value.compareTo(otherValue)).isEqualTo(1)
}

@Test
fun compareTo_quantityType_shouldReturnZero() {
val value = Quantity().setCode("h").setValue(10)
val otherValue = Quantity().setCode("h").setValue(10)

assertThat(value.compareTo(otherValue)).isEqualTo(0)
}

@Test
fun compareTo_quantityType_shouldReturnNegativeValue() {
val value = Quantity().setCode("h").setValue(10)
val otherValue = Quantity().setCode("h").setValue(20)

assertThat(value.compareTo(otherValue)).isEqualTo(-1)
}

@Test
fun compareTo_quantityWithDifferentCodes_shouldFail() {
assertFailsWith<IllegalArgumentException> {
val value = Quantity().setCode("h").setValue(10)
val otherValue = Quantity().setCode("kg").setValue(5)
value.compareTo(otherValue)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,31 +228,6 @@ class QuestionnaireItemEditTextDecimalViewHolderFactoryInstrumentedTest {
.isEqualTo("Minimum value allowed is:2.1")
}

@Test
@UiThreadTest
fun displayValidationResult_noError_shouldShowNoErrorMessage() {
viewHolder.bind(
QuestionnaireItemViewItem(
Questionnaire.QuestionnaireItemComponent().apply {
addExtension().apply {
url = "http:https://hl7.org/fhir/StructureDefinition/maxValue"
setValue(null)
}
},
QuestionnaireResponse.QuestionnaireResponseItemComponent().apply {
addAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = DecimalType("100.3")
}
)
}
) {}
)

assertThat(viewHolder.itemView.findViewById<TextInputLayout>(R.id.textInputLayout).error)
.isNull()
}

@Test
@UiThreadTest
fun bind_readOnly_shouldDisableView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.google.common.truth.Truth.assertThat
import java.math.BigDecimal
import kotlin.test.assertFailsWith
import org.hl7.fhir.r4.model.Quantity
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.QuestionnaireResponse
Expand Down Expand Up @@ -189,34 +188,6 @@ class QuestionnaireItemEditTextQuantityViewHolderFactoryInstrumentedTest {
assertThat(questionnaireItemViewItem.questionnaireResponseItem.answer.size).isEqualTo(0)
}

@Test
@UiThreadTest
fun displayValidationResult_shouldThrowNotImplementedError() {
assertFailsWith<NotImplementedError> {
viewHolder.bind(
QuestionnaireItemViewItem(
Questionnaire.QuestionnaireItemComponent().apply {
addExtension().apply {
url = "http:https://hl7.org/fhir/StructureDefinition/minValue"
setValue(Quantity(2.2))
}
addExtension().apply {
url = "http:https://hl7.org/fhir/StructureDefinition/maxValue"
setValue(Quantity(4.4))
}
},
QuestionnaireResponse.QuestionnaireResponseItemComponent().apply {
addAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = Quantity(3.3)
}
)
}
) {}
)
}
}

@Test
@UiThreadTest
fun displayValidationResult_error_shouldShowErrorMessage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package com.google.android.fhir.datacapture.enablement

import com.google.android.fhir.compareTo
import com.google.android.fhir.equals
import java.lang.IllegalStateException
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.QuestionnaireResponse

Expand Down Expand Up @@ -127,6 +127,18 @@ private val Questionnaire.QuestionnaireItemEnableWhenComponent.predicate:
Questionnaire.QuestionnaireItemOperator.NOT_EQUAL -> {
!equals(it.value, answer)
}
Questionnaire.QuestionnaireItemOperator.GREATER_THAN -> {
it.value > answer
}
Questionnaire.QuestionnaireItemOperator.GREATER_OR_EQUAL -> {
it.value >= answer
}
Questionnaire.QuestionnaireItemOperator.LESS_THAN -> {
it.value < answer
}
Questionnaire.QuestionnaireItemOperator.LESS_OR_EQUAL -> {
it.value <= answer
}
else -> throw NotImplementedError("Enable when operator $operator is not implemented.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,110 @@ class EnablementEvaluatorTest {
.isFalse()
}

@Test
fun evaluate_expectAnswerGreaterThanValue_someAnswerGreaterThanValue_shouldReturnTrue() {
evaluateEnableWhen(
behavior = null,
EnableWhen(
operator = Questionnaire.QuestionnaireItemOperator.GREATER_THAN,
expected = IntegerType(10),
actual = listOf(IntegerType(20))
)
)
.isTrue()
}

@Test
fun evaluate_expectAnswerGreaterThanValue_noAnswerGreaterThanValue_shouldReturnFalse() {
evaluateEnableWhen(
behavior = null,
EnableWhen(
operator = Questionnaire.QuestionnaireItemOperator.GREATER_THAN,
expected = IntegerType(10),
actual = listOf(IntegerType(5))
)
)
.isFalse()
}

@Test
fun evaluate_expectAnswerGreaterThanOrEqualToValue_someAnswerGreaterThanOrEqualToValue_shouldReturnTrue() {
evaluateEnableWhen(
behavior = null,
EnableWhen(
operator = Questionnaire.QuestionnaireItemOperator.GREATER_OR_EQUAL,
expected = IntegerType(10),
actual = listOf(IntegerType(10))
)
)
.isTrue()
}

@Test
fun evaluate_expectAnswerGreaterThanOrEqualToValue_noAnswerGreaterThanOrEqualToValue_shouldReturnFalse() {
evaluateEnableWhen(
behavior = null,
EnableWhen(
operator = Questionnaire.QuestionnaireItemOperator.GREATER_OR_EQUAL,
expected = IntegerType(10),
actual = listOf(IntegerType(5))
)
)
.isFalse()
}

@Test
fun evaluate_expectAnswerLessThanValue_someAnswerLessThanValue_shouldReturnTrue() {
evaluateEnableWhen(
behavior = null,
EnableWhen(
operator = Questionnaire.QuestionnaireItemOperator.LESS_THAN,
expected = IntegerType(10),
actual = listOf(IntegerType(5))
)
)
.isTrue()
}

@Test
fun evaluate_expectAnswerLessThanValue_noAnswerLessThanValue_shouldReturnFalse() {
evaluateEnableWhen(
behavior = null,
EnableWhen(
operator = Questionnaire.QuestionnaireItemOperator.LESS_THAN,
expected = IntegerType(10),
actual = listOf(IntegerType(20))
)
)
.isFalse()
}

@Test
fun evaluate_expectAnswerLessThanOrEqualToValue_someAnswerLessThanOrEqualToValue_shouldReturnTrue() {
evaluateEnableWhen(
behavior = null,
EnableWhen(
operator = Questionnaire.QuestionnaireItemOperator.LESS_OR_EQUAL,
expected = IntegerType(10),
actual = listOf(IntegerType(10))
)
)
.isTrue()
}

@Test
fun evaluate_expectAnswerLessThanOrEqualToValue_noAnswerLessThanOrEqualToValue_shouldReturnFalse() {
evaluateEnableWhen(
behavior = null,
EnableWhen(
operator = Questionnaire.QuestionnaireItemOperator.LESS_OR_EQUAL,
expected = IntegerType(10),
actual = listOf(IntegerType(20))
)
)
.isFalse()
}

@Test
fun evaluate_multipleEnableWhens_behaviorAny_noneSatisfied_shouldReturnFalse() {
evaluateEnableWhen(
Expand Down