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

Common library bug fixes and code refactoring #2082

Merged
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/Releases.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,7 +42,7 @@ object Releases {

object Common : LibraryArtifact {
override val artifactId = "common"
override val version = "0.1.0-alpha03"
override val version = "0.1.0-alpha04"
override val name = "Android FHIR Common Library"
}

Expand Down
8 changes: 4 additions & 4 deletions common/src/main/java/com/google/android/fhir/MoreTypes.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -65,9 +65,10 @@ operator fun Type.compareTo(value: Type): Int {
return this.dateTimeValue().value.compareTo(value.dateTimeValue().value)
}
this.fhirType().equals("Quantity") -> {
val quantity = UnitConverter.getCanonicalForm(UcumValue((this as Quantity).code, this.value))
val quantity =
UnitConverter.getCanonicalFormOrOriginal(UcumValue((this as Quantity).code, this.value))
val anotherQuantity =
UnitConverter.getCanonicalForm(UcumValue((value as Quantity).code, value.value))
UnitConverter.getCanonicalFormOrOriginal(UcumValue((value as Quantity).code, value.value))
if (quantity.code != anotherQuantity.code) {
throw IllegalArgumentException(
"Cannot compare different quantity codes: ${quantity.code} and ${anotherQuantity.code}"
Expand All @@ -79,7 +80,6 @@ operator fun Type.compareTo(value: Type): Int {
throw NotImplementedError()
}
}
return 0
}

private fun clearTimeFromDateValue(dateValue: Date): Date {
Expand Down
26 changes: 23 additions & 3 deletions common/src/main/java/com/google/android/fhir/UnitConverter.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,6 @@ package com.google.android.fhir
import java.lang.NullPointerException
import java.math.BigDecimal
import java.math.MathContext
import kotlin.jvm.Throws
import org.fhir.ucum.Decimal
import org.fhir.ucum.Pair
import org.fhir.ucum.UcumEssenceService
Expand All @@ -45,9 +44,10 @@ object UnitConverter {
* The canonical form is generated by normalizing [value] to UCUM base units, used to generate
* canonical matches on Quantity Search
*
* @throws ConverterException if fails to generate canonical matches
*
* For example a value of 1000 mm will return 1 m.
*/
@Throws(ConverterException::class)
fun getCanonicalForm(value: UcumValue): UcumValue {
try {
val pair =
Expand All @@ -63,6 +63,26 @@ object UnitConverter {
throw ConverterException("Missing numerical value in the canonical UCUM value", e)
}
}

/**
* Returns the canonical form of a UCUM Value if it is supported in Ucum library.
*
* The canonical form is generated by normalizing [value] to UCUM base units, used to generate
* canonical matches on Quantity Search, if fails to generate then returns original value
*
* For example a value of 1000 mm will return 1 m.
*/
fun getCanonicalFormOrOriginal(value: UcumValue): UcumValue {
return try {
getCanonicalForm(value)
} catch (e: ConverterException) {
val pair = Pair(Decimal(value.value.toPlainString()), value.code)
UcumValue(
pair.code,
pair.value.asDecimal().toBigDecimal(MathContext(value.value.precision()))
)
}
}
}

class ConverterException(message: String, cause: Throwable) : Exception(message, cause)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -48,4 +48,13 @@ class UnitConverterTest {
.hasMessageThat()
.isEqualTo("Missing numerical value in the canonical UCUM value")
}

@Test
fun `should return original code and value if fails to convert Cel to K`() {
val canonicalValue =
UnitConverter.getCanonicalFormOrOriginal(UcumValue("Cel", BigDecimal.valueOf(37.0)))

assertThat(canonicalValue.code).isEqualTo("Cel")
assertThat(canonicalValue.value.toDouble()).isEqualTo(37.0)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -40,7 +40,7 @@ internal object MaxDecimalPlacesValidator :
maxDecimalPlaces != null &&
answer.valueDecimalType.valueAsString.substringAfter(".").length > maxDecimalPlaces
},
{ extension: Extension, context: Context ->
messageGenerator = { extension: Extension, context: Context ->
context.getString(R.string.max_decimal_validation_error_msg, extension.value.primitiveValue())
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@ package com.google.android.fhir.datacapture.validation
import android.content.Context
import com.google.android.fhir.compareTo
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.extensions.displayString
import com.google.android.fhir.datacapture.extensions.valueOrCalculateValue
import org.hl7.fhir.r4.model.Extension
import org.hl7.fhir.r4.model.Questionnaire
Expand All @@ -36,10 +37,10 @@ internal object MaxValueValidator :
answer: QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent ->
answer.value > extension.value?.valueOrCalculateValue()!!
},
{ extension: Extension, context: Context ->
messageGenerator = { extension: Extension, context: Context ->
context.getString(
R.string.max_value_validation_error_msg,
extension.value?.valueOrCalculateValue()?.primitiveValue()
extension.value?.valueOrCalculateValue()?.displayString(context)
)
}
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@
package com.google.android.fhir.datacapture.validation

import android.content.Context
import com.google.android.fhir.datacapture.R
import org.hl7.fhir.r4.model.Extension
import org.hl7.fhir.r4.model.IntegerType
import org.hl7.fhir.r4.model.PrimitiveType
Expand All @@ -41,9 +42,8 @@ internal object MinLengthValidator :
(answer.value as PrimitiveType<*>).asStringValue().length <
(extension.value as IntegerType).value
},
messageGenerator = { extension: Extension, _: Context ->
("The minimum number of characters that are permitted in the answer is: " +
extension.value.primitiveValue())
messageGenerator = { extension: Extension, context: Context ->
context.getString(R.string.min_length_validation_error_msg, extension.value.primitiveValue())
jingtang10 marked this conversation as resolved.
Show resolved Hide resolved
}
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@ package com.google.android.fhir.datacapture.validation
import android.content.Context
import com.google.android.fhir.compareTo
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.extensions.displayString
import com.google.android.fhir.datacapture.extensions.valueOrCalculateValue
import org.hl7.fhir.r4.model.Extension
import org.hl7.fhir.r4.model.Questionnaire
Expand All @@ -35,10 +36,10 @@ internal object MinValueValidator :
answer: QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent ->
answer.value < extension.value?.valueOrCalculateValue()!!
},
{ extension: Extension, context: Context ->
messageGenerator = { extension: Extension, context: Context ->
context.getString(
R.string.min_value_validation_error_msg,
extension.value?.valueOrCalculateValue()?.primitiveValue()
extension.value?.valueOrCalculateValue()?.displayString(context)
)
}
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@
package com.google.android.fhir.datacapture.validation

import android.content.Context
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.extensions.asStringValue
import java.util.regex.Pattern
import java.util.regex.PatternSyntaxException
Expand Down Expand Up @@ -49,8 +50,8 @@ internal object RegexValidator :
false
}
},
{ extension: Extension, _: Context ->
"The answer doesn't match regular expression: " + extension.value.primitiveValue()
messageGenerator = { extension: Extension, context: Context ->
context.getString(R.string.regex_validation_error_msg, extension.value.primitiveValue())
}
)

Expand Down
14 changes: 12 additions & 2 deletions datacapture/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,24 @@
<string
name="min_value_validation_error_msg"
>Minimum value allowed is:<xliff:g id="min_value">%1$s</xliff:g> </string>
<string
name="max_value_validation_error_msg"
>Maximum value allowed is:<xliff:g id="max_value">%1$s</xliff:g> </string>
<string
name="min_length_validation_error_msg"
>The minimum number of characters that are permitted in the answer is: <xliff:g
id="min_length"
>%1$s</xliff:g> </string>
<string
name="max_decimal_validation_error_msg"
>The maximum number of decimal places that are permitted in the answer is: <xliff:g
id="max_value"
>%1$s</xliff:g> </string>
<string
name="max_value_validation_error_msg"
>Maximum value allowed is:<xliff:g id="max_value">%1$s</xliff:g> </string>
name="regex_validation_error_msg"
>The answer doesn\'t match regular expression: <xliff:g
id="answer_value"
>%1$s</xliff:g> </string>
<string
name="number_format_validation_error_msg"
>Only use (.) between two numbers. Other special characters are not supported.</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -95,7 +95,7 @@ internal class ResourceIndexer(
SearchParamType.REFERENCE ->
referenceIndex(searchParam, value)?.also { indexBuilder.addReferenceIndex(it) }
SearchParamType.QUANTITY ->
quantityIndex(searchParam, value)?.forEach { indexBuilder.addQuantityIndex(it) }
quantityIndex(searchParam, value).forEach { indexBuilder.addQuantityIndex(it) }
SearchParamType.URI -> uriIndex(searchParam, value)?.also { indexBuilder.addUriIndex(it) }
SearchParamType.SPECIAL -> specialIndex(value)?.also { indexBuilder.addPositionIndex(it) }
// TODO: Handle composite type https://github.com/google/android-fhir/issues/292.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -295,7 +295,8 @@ internal fun getConditionParamPair(
(prefix != ParamPrefixEnum.STARTS_AFTER && prefix != ParamPrefixEnum.ENDS_BEFORE)
) { "Prefix $prefix not allowed for Integer type" }
return when (prefix) {
ParamPrefixEnum.EQUAL, null -> {
ParamPrefixEnum.EQUAL,
null -> {
val precision = value.getRange()
ConditionParam(
"index_value >= ? AND index_value < ?",
Expand Down
2 changes: 1 addition & 1 deletion license-header.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down