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

Add config Composition section to support custom search parameters #3695

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.smartregister.fhircore.engine

import com.google.android.fhir.FhirEngine
import com.google.android.fhir.LocalChange
import com.google.android.fhir.SearchResult
import com.google.android.fhir.db.LocalChangeResourceReference
import com.google.android.fhir.search.Search
import com.google.android.fhir.sync.ConflictResolver
import com.google.android.fhir.sync.upload.SyncUploadProgress
import com.google.android.fhir.sync.upload.UploadRequestResult
import com.google.android.fhir.sync.upload.UploadStrategy
import java.time.OffsetDateTime
import kotlinx.coroutines.flow.Flow
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType

class FhirEngineWrapper(fhirEngineProducer: () -> FhirEngine) : FhirEngine {

private val mFhirEngine by lazy {
return@lazy fhirEngineProducer.invoke()
}

override suspend fun clearDatabase() = mFhirEngine.clearDatabase()

override suspend fun count(search: Search): Long = mFhirEngine.count(search)

override suspend fun create(vararg resource: Resource, isLocalOnly: Boolean): List<String> =
mFhirEngine.create(*resource, isLocalOnly = isLocalOnly)

override suspend fun delete(type: ResourceType, id: String) = mFhirEngine.delete(type, id)

override suspend fun get(type: ResourceType, id: String): Resource = mFhirEngine.get(type, id)

override suspend fun getLastSyncTimeStamp(): OffsetDateTime? = mFhirEngine.getLastSyncTimeStamp()

override suspend fun getLocalChanges(type: ResourceType, id: String): List<LocalChange> =
mFhirEngine.getLocalChanges(type, id)

override suspend fun getUnsyncedLocalChanges(): List<LocalChange> =
mFhirEngine.getUnsyncedLocalChanges()

override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) =
mFhirEngine.purge(type, id, forcePurge)

override suspend fun purge(type: ResourceType, ids: Set<String>, forcePurge: Boolean) =
mFhirEngine.purge(type, ids, forcePurge)

override suspend fun <R : Resource> search(search: Search): List<SearchResult<R>> =
mFhirEngine.search<R>(search)

@Deprecated("To be deprecated.")
override suspend fun syncDownload(
conflictResolver: ConflictResolver,
download: suspend () -> Flow<List<Resource>>,
) = mFhirEngine.syncDownload(conflictResolver, download)

@Deprecated("To be deprecated.")
override suspend fun syncUpload(
uploadStrategy: UploadStrategy,
upload:
suspend (List<LocalChange>, List<LocalChangeResourceReference>) -> Flow<UploadRequestResult>,
): Flow<SyncUploadProgress> = mFhirEngine.syncUpload(uploadStrategy, upload)

override suspend fun update(vararg resource: Resource) = mFhirEngine.update(*resource)

override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) =
mFhirEngine.withTransaction(block)
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid
import org.smartregister.fhircore.engine.util.extension.fileExtension
import org.smartregister.fhircore.engine.util.extension.generateMissingId
import org.smartregister.fhircore.engine.util.extension.interpolate
import org.smartregister.fhircore.engine.util.extension.retrieveCompositionSections
import org.smartregister.fhircore.engine.util.extension.retrieveCompositionSectionsExcludingCustomSearchParameters
import org.smartregister.fhircore.engine.util.extension.retrieveRelatedEntitySyncLocationState
import org.smartregister.fhircore.engine.util.extension.searchCompositionByIdentifier
import org.smartregister.fhircore.engine.util.extension.toTimeZoneString
Expand Down Expand Up @@ -267,7 +267,7 @@ constructor(

localCompositionResource.run {
val iconConfigs =
retrieveCompositionSections().filter {
retrieveCompositionSectionsExcludingCustomSearchParameters().filter {
it.focus.hasIdentifier() && isIconConfig(it.focus.identifier.value)
}
if (iconConfigs.isNotEmpty()) {
Expand Down Expand Up @@ -338,7 +338,8 @@ constructor(
}
}
} else {
composition.retrieveCompositionSections().forEach { sectionComponent ->
composition.retrieveCompositionSectionsExcludingCustomSearchParameters().forEach {
sectionComponent ->
if (sectionComponent.hasFocus()) {
addBinaryToConfigsJsonMap(
sectionComponent.focus,
Expand Down Expand Up @@ -435,7 +436,8 @@ constructor(
val parsedAppId = appId.substringBefore(TYPE_REFERENCE_DELIMITER).trim()
val compositionResource = fetchRemoteCompositionByAppId(parsedAppId)
compositionResource?.let { composition ->
val compositionSections = composition.retrieveCompositionSections()
val compositionSections =
composition.retrieveCompositionSectionsExcludingCustomSearchParameters()
val sectionComponentMap = mutableMapOf<String, MutableList<Composition.SectionComponent>>()
compositionSections.forEach { sectionComponent ->
if (sectionComponent.hasFocus() && sectionComponent.focus.hasReferenceElement()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
package org.smartregister.fhircore.engine.configuration.app

import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.Enumerations
import org.hl7.fhir.r4.model.ResourceType
import org.hl7.fhir.r4.model.SearchParameter
import org.smartregister.fhircore.engine.sync.ResourceTag
import org.smartregister.fhircore.engine.util.SharedPreferenceKey
import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
Expand Down Expand Up @@ -73,70 +71,7 @@ interface ConfigService {
return tags
}

/**
* Provide a list of custom search parameters.
*
* @return list of predefined custom search parameters.
*/
fun provideCustomSearchParameters(): List<SearchParameter> {
val activeGroupSearchParameter =
SearchParameter().apply {
url = "http://smartregister.org/SearchParameter/group-active"
addBase("Group")
name = ACTIVE_SEARCH_PARAM
code = ACTIVE_SEARCH_PARAM
type = Enumerations.SearchParamType.TOKEN
expression = "Group.active"
description = "Search the active field"
}

val flagStatusSearchParameter =
SearchParameter().apply {
url = "http://smartregister.org/SearchParameter/flag-status"
addBase("Flag")
name = STATUS_SEARCH_PARAM
code = STATUS_SEARCH_PARAM
type = Enumerations.SearchParamType.TOKEN
expression = "Flag.status"
description = "Search the status field"
}

val medicationSortSearchParameter =
SearchParameter().apply {
url = MEDICATION_SORT_URL
addBase("Medication")
name = SORT_SEARCH_PARAM
code = SORT_SEARCH_PARAM
type = Enumerations.SearchParamType.NUMBER
expression = "Medication.extension.where(url = '$MEDICATION_SORT_URL').value"
description = "Search the sort field"
}

val patientSearchParameter =
SearchParameter().apply {
url = "http://smartregister.org/SearchParameter/patient-search"
addBase("Patient")
name = SEARCH_PARAM
code = SEARCH_PARAM
type = Enumerations.SearchParamType.STRING
expression = "Patient.name.text | Patient.identifier.value"
description = "Search patients by name and identifier fields"
}

return listOf(
activeGroupSearchParameter,
flagStatusSearchParameter,
medicationSortSearchParameter,
patientSearchParameter,
)
}

companion object {
const val ACTIVE_SEARCH_PARAM = "active"
const val APP_VERSION = "AppVersion"
const val STATUS_SEARCH_PARAM = "status"
const val SORT_SEARCH_PARAM = "sort"
const val SEARCH_PARAM = "search"
const val MEDICATION_SORT_URL = "http://smartregister.org/SearchParameter/medication-sort"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.smartregister.fhircore.engine.configuration.customsearch

import org.hl7.fhir.r4.model.Bundle

interface ISearchParametersConfigStore {

suspend fun write(bundle: Bundle)

fun read(): Bundle?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.smartregister.fhircore.engine.configuration.customsearch

import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.Enumerations
import org.hl7.fhir.r4.model.ResourceType
import org.hl7.fhir.r4.model.SearchParameter

class SearchParametersConfigService(
private val store: ISearchParametersConfigStore,

Check warning on line 25 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L24-L25

Added lines #L24 - L25 were not covered by tests
) {

fun getCustomSearchParameters(): List<SearchParameter> {
return predefinedCustomSearchParameters + readSavedSearchParameters()

Check warning on line 29 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L29

Added line #L29 was not covered by tests
}

private fun readSavedSearchParameters(): List<SearchParameter> {
val searchParametersBundle = store.read() ?: return emptyList()

return searchParametersBundle.entry

Check warning on line 35 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L35

Added line #L35 was not covered by tests
.filter { it.resource.resourceType == ResourceType.SearchParameter }
.mapNotNull { it.resource as? SearchParameter }
}

suspend fun saveBundle(bundle: Bundle) {
store.write(bundle)

Check warning on line 41 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L41

Added line #L41 was not covered by tests
}

/** List of predefined custom search parameters. */
private val predefinedCustomSearchParameters: List<SearchParameter>
get() {
val activeGroupSearchParameter =
SearchParameter().apply {
url = "http://smartregister.org/SearchParameter/group-active"
addBase("Group")
name = ACTIVE_SEARCH_PARAM
code = ACTIVE_SEARCH_PARAM
type = Enumerations.SearchParamType.TOKEN
expression = "Group.active"
description = "Search the active field"

Check warning on line 55 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L47-L55

Added lines #L47 - L55 were not covered by tests
}

val flagStatusSearchParameter =
SearchParameter().apply {
url = "http://smartregister.org/SearchParameter/flag-status"
addBase("Flag")
name = STATUS_SEARCH_PARAM
code = STATUS_SEARCH_PARAM
type = Enumerations.SearchParamType.TOKEN
expression = "Flag.status"
description = "Search the status field"

Check warning on line 66 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L58-L66

Added lines #L58 - L66 were not covered by tests
}

val medicationSortSearchParameter =
SearchParameter().apply {
url = MEDICATION_SORT_URL
addBase("Medication")
name = SORT_SEARCH_PARAM
code = SORT_SEARCH_PARAM
type = Enumerations.SearchParamType.NUMBER
expression = "Medication.extension.where(url = '$MEDICATION_SORT_URL').value"
description = "Search the sort field"

Check warning on line 77 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L69-L77

Added lines #L69 - L77 were not covered by tests
}

val patientSearchParameter =
SearchParameter().apply {
url = "http://smartregister.org/SearchParameter/patient-search"
addBase("Patient")
name = SEARCH_PARAM
code = SEARCH_PARAM
type = Enumerations.SearchParamType.STRING
expression = "Patient.name.text | Patient.identifier.value"
description = "Search patients by name and identifier fields"

Check warning on line 88 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L80-L88

Added lines #L80 - L88 were not covered by tests
}

return listOf(
activeGroupSearchParameter,
flagStatusSearchParameter,
medicationSortSearchParameter,
patientSearchParameter,

Check warning on line 95 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/customsearch/SearchParametersConfigService.kt#L91-L95

Added lines #L91 - L95 were not covered by tests
)
}

companion object {
private const val ACTIVE_SEARCH_PARAM = "active"
private const val STATUS_SEARCH_PARAM = "status"
private const val SORT_SEARCH_PARAM = "sort"
private const val SEARCH_PARAM = "search"
private const val MEDICATION_SORT_URL =
"http://smartregister.org/SearchParameter/medication-sort"
}
}
Loading
Loading