From deb1cfd3ade41f6dbaa214e0432e6e852e7e0e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:11:10 +0300 Subject: [PATCH 001/110] Test single transaction save for questionnaires --- android/engine/build.gradle.kts | 1 + android/gradle/libs.versions.toml | 2 +- .../questionnaire/QuestionnaireViewModel.kt | 90 ++++++++++--------- 3 files changed, 49 insertions(+), 44 deletions(-) diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index 7393796830..58096ca653 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -201,6 +201,7 @@ dependencies { exclude(group = "com.google.android.fhir", module = "common") exclude(group = "com.google.android.fhir", module = "engine") exclude(group = "com.github.ben-manes.caffeine") + exclude(group = "org.smartregister", module = "engine") } api(libs.contrib.barcode) { isTransitive = true diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index fadfb80f09..7fdfbabf31 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -27,7 +27,7 @@ fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview12-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview11-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview11-MWCore-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" fhir-sdk-workflow = "0.1.0-alpha04-preview10-SNAPSHOT" foundation = "1.6.8" diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index ee2f3b9b5d..3f6a94b7a0 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -281,63 +281,67 @@ constructor( context = context, ) - saveExtractedResources( - bundle = bundle, - questionnaire = questionnaire, - questionnaireConfig = questionnaireConfig, - questionnaireResponse = currentQuestionnaireResponse, - context = context, - ) + val doSaveOperations: suspend () -> Unit = { + saveExtractedResources( + bundle = bundle, + questionnaire = questionnaire, + questionnaireConfig = questionnaireConfig, + questionnaireResponse = currentQuestionnaireResponse, + context = context, + ) - updateResourcesLastUpdatedProperty(actionParameters) + updateResourcesLastUpdatedProperty(actionParameters) - // Important to load subject resource to retrieve ID (as reference) correctly - val subjectIdType: IdType? = - if (currentQuestionnaireResponse.subject.reference.isNullOrEmpty()) { - null - } else { - IdType(currentQuestionnaireResponse.subject.reference) - } + // Important to load subject resource to retrieve ID (as reference) correctly + val subjectIdType: IdType? = + if (currentQuestionnaireResponse.subject.reference.isNullOrEmpty()) { + null + } else { + IdType(currentQuestionnaireResponse.subject.reference) + } - if (subjectIdType != null) { - val subject = - loadResource( - ResourceType.valueOf(subjectIdType.resourceType), - subjectIdType.idPart, - ) + if (subjectIdType != null) { + val subject = + loadResource( + ResourceType.valueOf(subjectIdType.resourceType), + subjectIdType.idPart, + ) - if (subject != null && !questionnaireConfig.isReadOnly()) { - val newBundle = bundle.copyBundle(currentQuestionnaireResponse) + if (subject != null && !questionnaireConfig.isReadOnly()) { + val newBundle = bundle.copyBundle(currentQuestionnaireResponse) - val extractedResources = newBundle.entry.map { it.resource } - validateWithFhirValidator(*extractedResources.toTypedArray()) + val extractedResources = newBundle.entry.map { it.resource } + validateWithFhirValidator(*extractedResources.toTypedArray()) - generateCarePlan( - subject = subject, - bundle = newBundle, - questionnaireConfig = questionnaireConfig, - ) - - withContext(dispatcherProvider.io()) { - executeCql( + generateCarePlan( subject = subject, bundle = newBundle, - questionnaire = questionnaire, questionnaireConfig = questionnaireConfig, ) - } - fhirCarePlanGenerator.conditionallyUpdateResourceStatus( - questionnaireConfig = questionnaireConfig, - subject = subject, - bundle = newBundle, - ) + withContext(dispatcherProvider.io()) { + executeCql( + subject = subject, + bundle = newBundle, + questionnaire = questionnaire, + questionnaireConfig = questionnaireConfig, + ) + } + + fhirCarePlanGenerator.conditionallyUpdateResourceStatus( + questionnaireConfig = questionnaireConfig, + subject = subject, + bundle = newBundle, + ) + } } - } - softDeleteResources(questionnaireConfig) + softDeleteResources(questionnaireConfig) + + retireUsedQuestionnaireUniqueId(questionnaireConfig, currentQuestionnaireResponse) + } - retireUsedQuestionnaireUniqueId(questionnaireConfig, currentQuestionnaireResponse) + defaultRepository.fhirEngine.withTransaction { doSaveOperations.invoke() } val idTypes = bundle.entry?.map { IdType(it.resource.resourceType.name, it.resource.logicalId) } From 8129afdfcbfa09f4d9bf10d4441902751c1f50bb Mon Sep 17 00:00:00 2001 From: Aurangzaib Umer Date: Fri, 23 Aug 2024 15:41:31 +0500 Subject: [PATCH 002/110] Caching questionnaires and their SM --- .../engine/task/FhirCarePlanGenerator.kt | 2 +- .../quest/ui/questionnaire/ContentCache.kt | 19 ++++++++++++ .../questionnaire/QuestionnaireViewModel.kt | 30 +++++++++++++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt index 6fee7dfe88..44581b5db2 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt @@ -214,7 +214,7 @@ constructor( } source.setParameter(Task.SP_PERIOD, period) source.setParameter(ActivityDefinition.SP_VERSION, IntegerType(index)) - + //need to cache these SM too val structureMap = fhirEngine.get(IdType(action.transform).idPart) structureMapUtilities.transform( transformSupportServices.simpleWorkerContext, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt new file mode 100644 index 0000000000..e92f201555 --- /dev/null +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt @@ -0,0 +1,19 @@ +package org.smartregister.fhircore.quest.ui.questionnaire + +import androidx.collection.LruCache +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.hl7.fhir.r4.model.Resource + +object ContentCache { + private val maxMemory: Int = (Runtime.getRuntime().maxMemory() / 1024).toInt() + private val cacheSize: Int = maxMemory / 8 + private val cache = LruCache(cacheSize) + + suspend fun saveResource(resourceId: String, resource: Resource) = + withContext(Dispatchers.IO) { cache.put("${resource::class.simpleName}/$resourceId", resource) } + + fun getResource(resourceId: String) = cache[resourceId] + + suspend fun invalidate() = withContext(Dispatchers.IO) { cache.evictAll() } +} \ No newline at end of file diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 12c43c1dc3..a955bc0fe5 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -61,6 +61,7 @@ import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComp import org.hl7.fhir.r4.model.RelatedPerson import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.ResourceType +import org.hl7.fhir.r4.model.StructureMap import org.smartregister.fhircore.engine.BuildConfig import org.smartregister.fhircore.engine.configuration.ConfigType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry @@ -146,7 +147,18 @@ constructor( questionnaireConfig: QuestionnaireConfig, ): Questionnaire? { if (questionnaireConfig.id.isEmpty() || questionnaireConfig.id.isBlank()) return null - return defaultRepository.loadResource(questionnaireConfig.id) + var questionnaire = ContentCache.getResource(ResourceType.Questionnaire.name + "/" + questionnaireConfig.id)?.copy() + if (questionnaire == null) { + questionnaire = defaultRepository.loadResource(questionnaireConfig.id)?.also { ques -> + ContentCache.saveResource( + questionnaireConfig.id, + ques.copy(), + ) + } + } + + + return questionnaire as Questionnaire } /** @@ -631,7 +643,7 @@ constructor( transformSupportServices = transformSupportServices, structureMapProvider = { structureMapUrl: String?, _: IWorkerContext -> structureMapUrl?.substringAfterLast("/")?.let { - defaultRepository.loadResource(it) + fetchStructureMap(it) } }, ), @@ -661,6 +673,20 @@ constructor( } .getOrDefault(Bundle()) + + private suspend fun fetchStructureMap(structureMapUrl: String?): StructureMap? { + var structureMap: Resource? = null + structureMapUrl?.substringAfterLast("/")?.run { + structureMap = ContentCache.getResource(ResourceType.StructureMap.name + "/" + this) ?.let { + defaultRepository.loadResource(this)?.also { + it.let { ContentCache.saveResource(this, it) } + } + } + } + return structureMap as StructureMap? + } + + /** * This function saves [QuestionnaireResponse] as draft if any of the [QuestionnaireResponse.item] * has an answer. From e9221d1f45a34f1cb2483784b9565a443a26f818 Mon Sep 17 00:00:00 2001 From: Aurangzaib Umer Date: Fri, 23 Aug 2024 15:55:26 +0500 Subject: [PATCH 003/110] spotless ran --- .../engine/task/FhirCarePlanGenerator.kt | 2 +- .../quest/ui/questionnaire/ContentCache.kt | 32 +++++++++++---- .../questionnaire/QuestionnaireViewModel.kt | 40 ++++++++----------- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt index 44581b5db2..8f1a0f1eeb 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt @@ -214,7 +214,7 @@ constructor( } source.setParameter(Task.SP_PERIOD, period) source.setParameter(ActivityDefinition.SP_VERSION, IntegerType(index)) - //need to cache these SM too + // need to cache these SM too val structureMap = fhirEngine.get(IdType(action.transform).idPart) structureMapUtilities.transform( transformSupportServices.simpleWorkerContext, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt index e92f201555..78cf962c2f 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt @@ -1,3 +1,19 @@ +/* + * 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.quest.ui.questionnaire import androidx.collection.LruCache @@ -6,14 +22,14 @@ import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.Resource object ContentCache { - private val maxMemory: Int = (Runtime.getRuntime().maxMemory() / 1024).toInt() - private val cacheSize: Int = maxMemory / 8 - private val cache = LruCache(cacheSize) + private val maxMemory: Int = (Runtime.getRuntime().maxMemory() / 1024).toInt() + private val cacheSize: Int = maxMemory / 8 + private val cache = LruCache(cacheSize) - suspend fun saveResource(resourceId: String, resource: Resource) = - withContext(Dispatchers.IO) { cache.put("${resource::class.simpleName}/$resourceId", resource) } + suspend fun saveResource(resourceId: String, resource: Resource) = + withContext(Dispatchers.IO) { cache.put("${resource::class.simpleName}/$resourceId", resource) } - fun getResource(resourceId: String) = cache[resourceId] + fun getResource(resourceId: String) = cache[resourceId] - suspend fun invalidate() = withContext(Dispatchers.IO) { cache.evictAll() } -} \ No newline at end of file + suspend fun invalidate() = withContext(Dispatchers.IO) { cache.evictAll() } +} diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index a955bc0fe5..1deb62c811 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -147,17 +147,19 @@ constructor( questionnaireConfig: QuestionnaireConfig, ): Questionnaire? { if (questionnaireConfig.id.isEmpty() || questionnaireConfig.id.isBlank()) return null - var questionnaire = ContentCache.getResource(ResourceType.Questionnaire.name + "/" + questionnaireConfig.id)?.copy() + var questionnaire = + ContentCache.getResource(ResourceType.Questionnaire.name + "/" + questionnaireConfig.id) + ?.copy() if (questionnaire == null) { - questionnaire = defaultRepository.loadResource(questionnaireConfig.id)?.also { ques -> - ContentCache.saveResource( - questionnaireConfig.id, - ques.copy(), - ) - } + questionnaire = + defaultRepository.loadResource(questionnaireConfig.id)?.also { ques -> + ContentCache.saveResource( + questionnaireConfig.id, + ques.copy(), + ) + } } - return questionnaire as Questionnaire } @@ -642,8 +644,12 @@ constructor( StructureMapExtractionContext( transformSupportServices = transformSupportServices, structureMapProvider = { structureMapUrl: String?, _: IWorkerContext -> - structureMapUrl?.substringAfterLast("/")?.let { - fetchStructureMap(it) + structureMapUrl?.substringAfterLast("/")?.let { smID -> + ContentCache.getResource(smID)?.let { + defaultRepository.loadResource(smID)?.also { + it.let { ContentCache.saveResource(smID, it) } + } + } } }, ), @@ -673,20 +679,6 @@ constructor( } .getOrDefault(Bundle()) - - private suspend fun fetchStructureMap(structureMapUrl: String?): StructureMap? { - var structureMap: Resource? = null - structureMapUrl?.substringAfterLast("/")?.run { - structureMap = ContentCache.getResource(ResourceType.StructureMap.name + "/" + this) ?.let { - defaultRepository.loadResource(this)?.also { - it.let { ContentCache.saveResource(this, it) } - } - } - } - return structureMap as StructureMap? - } - - /** * This function saves [QuestionnaireResponse] as draft if any of the [QuestionnaireResponse.item] * has an answer. From 22a908fbdc4203c12fc2388f0914f36ee6bc0b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 20 Aug 2024 04:18:46 +0300 Subject: [PATCH 004/110] Fetch search results in batches for when loading all --- .../engine/data/local/DefaultRepository.kt | 18 ++++----- .../engine/p2p/dao/BaseP2PTransferDao.kt | 3 +- .../engine/task/FhirCarePlanGenerator.kt | 4 +- .../engine/task/FhirCompleteCarePlanWorker.kt | 4 +- .../fhircore/engine/task/FhirResourceUtil.kt | 8 ++-- .../util/extension/FhirEngineExtension.kt | 37 +++++++++++++++++-- .../util/extension/MeasureExtensions.kt | 2 +- .../util/extension/ResourceExtension.kt | 4 +- .../engine/task/FhirCarePlanGeneratorTest.kt | 12 +++--- .../questionnaire/QuestionnaireViewModel.kt | 4 +- .../measure/worker/MeasureReportWorker.kt | 4 +- 11 files changed, 64 insertions(+), 36 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 85b6059587..9f9e7e225b 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -36,7 +36,6 @@ import com.google.android.fhir.search.filter.TokenParamFilterCriterion import com.google.android.fhir.search.has import com.google.android.fhir.search.include import com.google.android.fhir.search.revInclude -import com.google.android.fhir.search.search import com.jayway.jsonpath.Configuration import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.Option @@ -86,6 +85,7 @@ import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference +import org.smartregister.fhircore.engine.util.extension.batchedSearch import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.extractId import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid @@ -136,7 +136,7 @@ constructor( ): List = withContext(dispatcherProvider.io()) { fhirEngine - .search { + .batchedSearch { filterByResourceTypeId(token, subjectType, subjectId) dataQueries.forEach { filterBy( @@ -149,7 +149,7 @@ constructor( } suspend inline fun search(search: Search) = - fhirEngine.search(search).map { it.resource } + fhirEngine.batchedSearch(search).map { it.resource } suspend inline fun count(search: Search) = fhirEngine.count(search) @@ -265,14 +265,14 @@ constructor( suspend fun loadManagingEntity(group: Group) = group.managingEntity?.let { reference -> fhirEngine - .search { + .batchedSearch { filter(RelatedPerson.RES_ID, { value = of(reference.extractId()) }) } .map { it.resource } .firstOrNull() ?.let { relatedPerson -> fhirEngine - .search { + .batchedSearch { filter( Patient.RES_ID, { value = of(relatedPerson.patient.extractId()) }, @@ -689,7 +689,7 @@ constructor( configComputedRuleValues: Map, ) { kotlin - .runCatching { fhirEngine.search(search) } + .runCatching { fhirEngine.batchedSearch(search) } .onSuccess { searchResult -> searchResult.forEach { currentSearchResult -> val includedResources: Map>? = @@ -789,7 +789,7 @@ constructor( configComputedRuleValues = computedValuesMap, ) } - val resources = fhirEngine.search(search).map { it.resource } + val resources = fhirEngine.batchedSearch(search).map { it.resource } val filteredResources = filterResourcesByFhirPathExpression( resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, @@ -994,7 +994,7 @@ constructor( kotlin .runCatching { val searchTime = System.currentTimeMillis() - val result = fhirEngine.search(search) + val result = fhirEngine.batchedSearch(search) Timber.w( "It took ${(System.currentTimeMillis() - searchTime) / 1000} second(s) to search resources of type ${baseResourceConfig.resource}", ) @@ -1161,7 +1161,7 @@ constructor( private suspend fun retrieveSubLocations(locationId: String) = withContext(dispatcherProvider.io()) { fhirEngine - .search( + .batchedSearch( Search(type = ResourceType.Location).apply { filter( Location.PARTOF, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/BaseP2PTransferDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/BaseP2PTransferDao.kt index 2893539540..474f2c0da8 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/BaseP2PTransferDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/BaseP2PTransferDao.kt @@ -36,6 +36,7 @@ import org.smartregister.fhircore.engine.configuration.ConfigType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.app.ApplicationConfiguration import org.smartregister.fhircore.engine.util.DispatcherProvider +import org.smartregister.fhircore.engine.util.extension.batchedSearch import org.smartregister.fhircore.engine.util.extension.isValidResourceType import org.smartregister.fhircore.engine.util.extension.resourceClassType import org.smartregister.p2p.model.RecordCount @@ -108,7 +109,7 @@ constructor( count = batchSize from = offset } - fhirEngine.search(search) + fhirEngine.batchedSearch(search) } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt index 6fee7dfe88..be6a694b6a 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt @@ -23,7 +23,6 @@ import ca.uhn.fhir.util.TerserUtil import com.google.android.fhir.FhirEngine import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.get -import com.google.android.fhir.search.search import dagger.hilt.android.qualifiers.ApplicationContext import java.util.Date import javax.inject.Inject @@ -60,6 +59,7 @@ import org.smartregister.fhircore.engine.configuration.event.EventType import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.util.extension.addResourceParameter import org.smartregister.fhircore.engine.util.extension.asReference +import org.smartregister.fhircore.engine.util.extension.batchedSearch import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.extractFhirpathDuration import org.smartregister.fhircore.engine.util.extension.extractFhirpathPeriod @@ -120,7 +120,7 @@ constructor( // Only one CarePlan per plan, update or init a new one if not exists val output = fhirEngine - .search { + .batchedSearch { filter( CarePlan.INSTANTIATES_CANONICAL, { value = planDefinition.referenceValue() }, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCompleteCarePlanWorker.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCompleteCarePlanWorker.kt index 0ff00e5ced..050173774b 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCompleteCarePlanWorker.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCompleteCarePlanWorker.kt @@ -20,7 +20,6 @@ import android.content.Context import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.google.android.fhir.search.search import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.withContext @@ -33,6 +32,7 @@ import org.smartregister.fhircore.engine.configuration.app.ApplicationConfigurat import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper +import org.smartregister.fhircore.engine.util.extension.batchedSearch import org.smartregister.fhircore.engine.util.extension.extractId import org.smartregister.fhircore.engine.util.extension.lastOffset import org.smartregister.fhircore.engine.util.getLastOffset @@ -94,7 +94,7 @@ constructor( suspend fun getCarePlans(batchSize: Int, lastOffset: Int) = defaultRepository.fhirEngine - .search { + .batchedSearch { filter( CarePlan.STATUS, { value = of(CarePlan.CarePlanStatus.DRAFT.toCode()) }, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirResourceUtil.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirResourceUtil.kt index 57228e3153..8f6674d1db 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirResourceUtil.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirResourceUtil.kt @@ -21,7 +21,6 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.get import com.google.android.fhir.search.filter.TokenParamFilterCriterion -import com.google.android.fhir.search.search import dagger.hilt.android.qualifiers.ApplicationContext import java.util.Date import javax.inject.Inject @@ -39,6 +38,7 @@ import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.app.ApplicationConfiguration import org.smartregister.fhircore.engine.configuration.event.EventType import org.smartregister.fhircore.engine.data.local.DefaultRepository +import org.smartregister.fhircore.engine.util.extension.batchedSearch import org.smartregister.fhircore.engine.util.extension.executionStartIsBeforeOrToday import org.smartregister.fhircore.engine.util.extension.expiredConcept import org.smartregister.fhircore.engine.util.extension.extractId @@ -66,7 +66,7 @@ constructor( Timber.i("Fetch and expire overdue tasks") val tasksResult = fhirEngine - .search { + .batchedSearch { filter( Task.STATUS, { value = of(TaskStatus.REQUESTED.toCoding()) }, @@ -148,7 +148,7 @@ constructor( val tasks = defaultRepository.fhirEngine - .search { + .batchedSearch { filter( Task.STATUS, { value = of(TaskStatus.REQUESTED.toCoding()) }, @@ -235,7 +235,7 @@ constructor( suspend fun closeResourcesRelatedToCompletedServiceRequests() { Timber.i("Fetch completed service requests and close related resources") defaultRepository.fhirEngine - .search { + .batchedSearch { filter( ServiceRequest.STATUS, { value = of(ServiceRequest.ServiceRequestStatus.COMPLETED.toCode()) }, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtension.kt index 3688624ac4..a8042951f2 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtension.kt @@ -18,9 +18,10 @@ package org.smartregister.fhircore.engine.util.extension import ca.uhn.fhir.util.UrlUtil import com.google.android.fhir.FhirEngine +import com.google.android.fhir.SearchResult import com.google.android.fhir.db.ResourceNotFoundException import com.google.android.fhir.get -import com.google.android.fhir.search.search +import com.google.android.fhir.search.Search import com.google.android.fhir.workflow.FhirOperator import org.hl7.fhir.r4.model.Composition import org.hl7.fhir.r4.model.IdType @@ -40,7 +41,7 @@ suspend inline fun FhirEngine.loadResource(resourceId: St } suspend fun FhirEngine.searchCompositionByIdentifier(identifier: String): Composition? = - this.search { + this.batchedSearch { filter(Composition.IDENTIFIER, { value = of(Identifier().apply { value = identifier }) }) } .map { it.resource } @@ -50,7 +51,9 @@ suspend fun FhirEngine.loadLibraryAtPath(fhirOperator: FhirOperator, path: Strin // resource path could be Library/123 OR something like http://fhir.labs.common/Library/123 val library = runCatching { get(IdType(path).idPart) }.getOrNull() - ?: search { filter(Library.URL, { value = path }) }.map { it.resource }.firstOrNull() + ?: batchedSearch { filter(Library.URL, { value = path }) } + .map { it.resource } + .firstOrNull() } suspend fun FhirEngine.loadLibraryAtPath( @@ -72,7 +75,7 @@ suspend fun FhirEngine.loadCqlLibraryBundle(fhirOperator: FhirOperator, measureP // resource path could be Measure/123 OR something like http://fhir.labs.common/Measure/123 val measure: Measure? = if (UrlUtil.isValid(measurePath)) { - search { filter(Measure.URL, { value = measurePath }) } + batchedSearch { filter(Measure.URL, { value = measurePath }) } .map { it.resource } .firstOrNull() } else { @@ -93,3 +96,29 @@ suspend fun FhirEngine.countUnSyncedResources() = .groupingBy { it.resourceType.spaceByUppercase() } .eachCount() .map { it.key to it.value } + +suspend fun FhirEngine.batchedSearch(search: Search) = + if (search.count != null) { + this.search(search) + } else { + val result = mutableListOf>() + var offset = search.from ?: 0 + val pageCount = 100 + do { + search.from = offset + search.count = pageCount + val searchResults = this.search(search) + result += searchResults + offset += searchResults.size + } while (searchResults.isNotEmpty()) + + result + } + +suspend inline fun FhirEngine.batchedSearch( + init: Search.() -> Unit, +): List> { + val search = Search(type = R::class.java.newInstance().resourceType) + search.init() + return this.batchedSearch(search) +} diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/MeasureExtensions.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/MeasureExtensions.kt index dd733712c4..4202f17b3c 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/MeasureExtensions.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/MeasureExtensions.kt @@ -165,5 +165,5 @@ suspend inline fun FhirEngine.retrievePreviouslyGeneratedMeasureReports( search.filter(MeasureReport.MEASURE, { value = measureUrl }) subjects.forEach { search.filter(MeasureReport.SUBJECT, { value = it }) } - return this.search(search).map { it.resource } + return this.batchedSearch(search).map { it.resource } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt index 9c08e1e1a4..9d0371a77d 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt @@ -23,7 +23,6 @@ import ca.uhn.fhir.rest.gclient.ReferenceClientParam import com.google.android.fhir.datacapture.extensions.createQuestionnaireResponseItem import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.get -import com.google.android.fhir.search.search import java.time.Duration import java.time.temporal.ChronoUnit import java.util.Date @@ -64,7 +63,6 @@ import org.hl7.fhir.r4.model.StructureMap import org.hl7.fhir.r4.model.Task import org.hl7.fhir.r4.model.Timing import org.hl7.fhir.r4.model.Type -import org.hl7.fhir.r4.model.codesystems.AdministrativeGender import org.joda.time.Instant import org.json.JSONException import org.json.JSONObject @@ -486,7 +484,7 @@ suspend fun Task.updateDependentTaskDueDate( return apply { val dependentTasks = defaultRepository.fhirEngine - .search { + .batchedSearch { filter( referenceParameter = ReferenceClientParam(PARTOF), { value = id }, diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt index 589a5494c6..d276880a9a 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt @@ -28,7 +28,6 @@ import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.get import com.google.android.fhir.knowledge.KnowledgeManager import com.google.android.fhir.search.Search -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 @@ -117,6 +116,7 @@ import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.REFERENCE import org.smartregister.fhircore.engine.util.extension.SDF_YYYY_MM_DD import org.smartregister.fhircore.engine.util.extension.asReference +import org.smartregister.fhircore.engine.util.extension.batchedSearch import org.smartregister.fhircore.engine.util.extension.decodeResourceFromString import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.extractId @@ -1784,7 +1784,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { } val bundle = Bundle().apply { addEntry().resource = patient } coEvery { - fhirEngine.search { + fhirEngine.batchedSearch { filter( CarePlan.INSTANTIATES_CANONICAL, { value = "${PlanDefinition().fhirType()}/plandef-1" }, @@ -1836,7 +1836,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { } val bundle = Bundle().apply { addEntry().resource = patient } coEvery { - fhirEngine.search { + fhirEngine.batchedSearch { filter( CarePlan.INSTANTIATES_CANONICAL, { value = "${PlanDefinition().fhirType()}/plandef-1" }, @@ -2088,7 +2088,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { input = listOf(Task.ParameterComponent(CodeableConcept(), StringType("9"))) } coEvery { - fhirEngine.search { + fhirEngine.batchedSearch { filter( referenceParameter = ReferenceClientParam("part-of"), { value = opv1.id.extractLogicalIdUuid() }, @@ -2104,7 +2104,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { ), ) coEvery { - fhirEngine.search { + fhirEngine.batchedSearch { filter( referenceParameter = ReferenceClientParam("part-of"), { value = immunizationResource.id.extractLogicalIdUuid() }, @@ -2112,7 +2112,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { } } returns listOf(SearchResult(resource = immunizationResource, null, null)) coEvery { - fhirEngine.search { + fhirEngine.batchedSearch { filter( referenceParameter = ReferenceClientParam("part-of"), { value = encounter.id.extractLogicalIdUuid() }, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 12c43c1dc3..24d21ff705 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -33,7 +33,6 @@ import com.google.android.fhir.datacapture.validation.Valid import com.google.android.fhir.db.ResourceNotFoundException import com.google.android.fhir.search.Search import com.google.android.fhir.search.filter.TokenParamFilterCriterion -import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.lifecycle.HiltViewModel import java.util.Date @@ -82,6 +81,7 @@ import org.smartregister.fhircore.engine.util.extension.appendOrganizationInfo import org.smartregister.fhircore.engine.util.extension.appendPractitionerInfo import org.smartregister.fhircore.engine.util.extension.appendRelatedEntityLocation import org.smartregister.fhircore.engine.util.extension.asReference +import org.smartregister.fhircore.engine.util.extension.batchedSearch import org.smartregister.fhircore.engine.util.extension.checkResourceValid import org.smartregister.fhircore.engine.util.extension.clearText import org.smartregister.fhircore.engine.util.extension.cqfLibraryUrls @@ -781,7 +781,7 @@ constructor( if (libraryFilters.isNotEmpty()) { defaultRepository.fhirEngine - .search { + .batchedSearch { filter( Resource.RES_ID, *libraryFilters.toTypedArray(), diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt index c6f272384c..3c0c481f40 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt @@ -25,7 +25,6 @@ import androidx.work.WorkManager import androidx.work.WorkerParameters import androidx.work.workDataOf import com.google.android.fhir.FhirEngine -import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -45,6 +44,7 @@ import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.extension.SDFHH_MM import org.smartregister.fhircore.engine.util.extension.SDF_YYYY_MM_DD +import org.smartregister.fhircore.engine.util.extension.batchedSearch import org.smartregister.fhircore.engine.util.extension.firstDayOfMonth import org.smartregister.fhircore.engine.util.extension.formatDate import org.smartregister.fhircore.engine.util.extension.lastDayOfMonth @@ -78,7 +78,7 @@ constructor( Timber.w("started MeasureReportWorker") fhirEngine - .search {} + .batchedSearch {} .map { it.resource } .forEach { monthList?.forEachIndexed { index, date -> From 5022fa8623a1db440f1f9ca0373796aa82edde65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:20:19 +0300 Subject: [PATCH 005/110] Fix infinite loop for mocks with FhirEngine#search in tests --- .../fhircore/engine/util/extension/FhirEngineExtension.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtension.kt index a8042951f2..ff4b014e10 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtension.kt @@ -110,7 +110,7 @@ suspend fun FhirEngine.batchedSearch(search: Search) = val searchResults = this.search(search) result += searchResults offset += searchResults.size - } while (searchResults.isNotEmpty()) + } while (searchResults.size == pageCount) result } From 6ee690dd0c382323d6f07f78b42d7d4f5b465c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:20:03 +0300 Subject: [PATCH 006/110] [WIP] FhirEngine search integration tests --- .../extension/FhirEngineExtensionKtTest.kt | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 android/engine/src/androidTest/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtensionKtTest.kt diff --git a/android/engine/src/androidTest/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtensionKtTest.kt b/android/engine/src/androidTest/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtensionKtTest.kt new file mode 100644 index 0000000000..57d89118c3 --- /dev/null +++ b/android/engine/src/androidTest/java/org/smartregister/fhircore/engine/util/extension/FhirEngineExtensionKtTest.kt @@ -0,0 +1,85 @@ +/* + * 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.util.extension + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.filters.MediumTest +import com.google.android.fhir.FhirEngine +import com.google.android.fhir.FhirEngineConfiguration +import com.google.android.fhir.FhirEngineProvider +import com.google.android.fhir.search.search +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.hl7.fhir.r4.model.Patient +import org.hl7.fhir.r4.model.Questionnaire +import org.junit.After +import org.junit.Before +import org.junit.Test + +@MediumTest +class FhirEngineExtensionKtTest { + + private val context = ApplicationProvider.getApplicationContext() + private lateinit var fhirEngine: FhirEngine + + @Before + fun setUp() { + FhirEngineProvider.init(FhirEngineConfiguration(testMode = true)) + fhirEngine = FhirEngineProvider.getInstance(context) + + val patients = (0..1000).map { Patient().apply { id = "test-patient-$it" } } + val questionnaires = (0..3).map { Questionnaire().apply { id = "test-questionnaire-$it" } } + runBlocking { fhirEngine.create(*patients.toTypedArray(), *questionnaires.toTypedArray()) } + } + + @After + fun tearDown() { + runBlocking { fhirEngine.clearDatabase() } + FhirEngineProvider.cleanup() + } + + @Test + fun test_search_time() { + runBlocking { + launch { + fhirEngine.search {} + println("Load patients") + } + + launch { + fhirEngine.search {} + println("Load questionnaires") + } + } + } + + @Test + fun test_batchedSearch_time() { + runBlocking { + launch { + fhirEngine.batchedSearch {} + println("Load patients..2") + } + + launch { + fhirEngine.search {} + println("Load questionnaires..2") + } + } + } +} From ae166a4113d04316cd751e7d0545d695722e32f7 Mon Sep 17 00:00:00 2001 From: Aurangzaib Umer Date: Wed, 28 Aug 2024 19:41:51 +0500 Subject: [PATCH 007/110] spotless ran --- .../fhircore/quest/ui/questionnaire/ContentCache.kt | 10 ++++++++-- .../quest/ui/questionnaire/QuestionnaireViewModel.kt | 11 +++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt index 78cf962c2f..96451b46c2 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt @@ -20,6 +20,7 @@ import androidx.collection.LruCache import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.Resource +import timber.log.Timber object ContentCache { private val maxMemory: Int = (Runtime.getRuntime().maxMemory() / 1024).toInt() @@ -27,9 +28,14 @@ object ContentCache { private val cache = LruCache(cacheSize) suspend fun saveResource(resourceId: String, resource: Resource) = - withContext(Dispatchers.IO) { cache.put("${resource::class.simpleName}/$resourceId", resource) } + withContext(Dispatchers.IO) { + cache.put("${resource::class.simpleName}/$resourceId", resource) + Timber.i("ContentCache:saveResource: $resourceId") + } - fun getResource(resourceId: String) = cache[resourceId] + fun getResource(resourceId: String): Resource? { + return cache[resourceId]?.also { Timber.i("ContentCache:getResource: $resourceId") } + } suspend fun invalidate() = withContext(Dispatchers.IO) { cache.evictAll() } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 1deb62c811..17acd4737f 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -645,11 +645,14 @@ constructor( transformSupportServices = transformSupportServices, structureMapProvider = { structureMapUrl: String?, _: IWorkerContext -> structureMapUrl?.substringAfterLast("/")?.let { smID -> - ContentCache.getResource(smID)?.let { - defaultRepository.loadResource(smID)?.also { - it.let { ContentCache.saveResource(smID, it) } - } + ContentCache.getResource(ResourceType.StructureMap.name + "/" + smID)?.let { + it as StructureMap } + ?: run { + defaultRepository.loadResource(smID)?.also { + ContentCache.saveResource(smID, it) + } + } } }, ), From 79f664de385bffcddf08e0672969a03ce47c671c Mon Sep 17 00:00:00 2001 From: Aurangzaib Umer Date: Mon, 2 Sep 2024 13:06:29 +0500 Subject: [PATCH 008/110] updated tests --- .../fhircore/quest/ContentCacheTest.kt | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 android/quest/src/test/java/org/smartregister/fhircore/quest/ContentCacheTest.kt diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ContentCacheTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ContentCacheTest.kt new file mode 100644 index 0000000000..5d2b18c0c3 --- /dev/null +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ContentCacheTest.kt @@ -0,0 +1,89 @@ +/* + * 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.quest + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.hl7.fhir.r4.model.Questionnaire +import org.hl7.fhir.r4.model.Resource +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.smartregister.fhircore.quest.ui.questionnaire.ContentCache + +@OptIn(ExperimentalCoroutinesApi::class) +class ContentCacheTest { + + private val testDispatcher = StandardTestDispatcher() + private val resourceId = "123" + private val mockResource: Resource = Questionnaire() + + @Before + fun setUp() { + Dispatchers.setMain(testDispatcher) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun `saveResource should store resource in cache`() = runTest { + ContentCache.saveResource(resourceId, mockResource) + advanceUntilIdle() // Ensure coroutine has finished + + val cachedResource = ContentCache.getResource("${mockResource::class.simpleName}/$resourceId") + assertNotNull(cachedResource) + assertEquals(mockResource, cachedResource) + } + + @Test + fun `getResource should return the correct resource from cache`() = runTest { + ContentCache.saveResource(resourceId, mockResource) + advanceUntilIdle() // Ensure coroutine has finished + + val result = ContentCache.getResource("${mockResource::class.simpleName}/$resourceId") + assertEquals(mockResource, result) + } + + @Test + fun `getResource should return null if resource does not exist`() = runTest { + val result = ContentCache.getResource("non_existing_id") + assertNull(result) + } + + @Test + fun `invalidate should clear all resources from cache`() = runTest { + ContentCache.saveResource(resourceId, mockResource) + advanceUntilIdle() // Ensure coroutine has finished + + ContentCache.invalidate() + advanceUntilIdle() // Ensure coroutine has finished + + val result = ContentCache.getResource("${mockResource::class.simpleName}/$resourceId") + assertNull(result) + } +} From c6be824e3e63235e233bdfa8334289af0c8d9e70 Mon Sep 17 00:00:00 2001 From: Aurangzaib Umer Date: Thu, 5 Sep 2024 14:30:38 +0500 Subject: [PATCH 009/110] WIP tests updated --- .../quest/ui/questionnaire/ContentCache.kt | 2 ++ .../QuestionnaireViewModelTest.kt | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt index 96451b46c2..beefa869c3 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt @@ -27,12 +27,14 @@ object ContentCache { private val cacheSize: Int = maxMemory / 8 private val cache = LruCache(cacheSize) + @JvmStatic suspend fun saveResource(resourceId: String, resource: Resource) = withContext(Dispatchers.IO) { cache.put("${resource::class.simpleName}/$resourceId", resource) Timber.i("ContentCache:saveResource: $resourceId") } + @JvmStatic fun getResource(resourceId: String): Resource? { return cache[resourceId]?.also { Timber.i("ContentCache:getResource: $resourceId") } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 9260bb8116..cfca7056eb 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -633,6 +633,26 @@ class QuestionnaireViewModelTest : RobolectricTest() { Assert.assertEquals(questionnaireConfig.id, questionnaire?.id?.extractLogicalIdUuid()) } + @Test + fun testRetrieveQuestionnaireShouldReturnValidQuestionnaireFromCache() = runBlocking { + coEvery { fhirEngine.get(ResourceType.Questionnaire, questionnaireConfig.id) } returns + samplePatientRegisterQuestionnaire + + ContentCache.saveResource(questionnaireConfig.id, samplePatientRegisterQuestionnaire) + + val questionnaire = + questionnaireViewModel.retrieveQuestionnaire( + questionnaireConfig = questionnaireConfig, + ) + + Assert.assertEquals( + samplePatientRegisterQuestionnaire, + ContentCache.getResource(ResourceType.Questionnaire.name + "/" + questionnaireConfig.id) + ) + Assert.assertNotNull(questionnaire) + Assert.assertEquals(questionnaireConfig.id, questionnaire?.id?.extractLogicalIdUuid()) + } + @Test fun testPopulateQuestionnaireShouldPrePopulatedQuestionnaireWithComputedValues() = runTest { val questionnaireViewModelInstance = From 0381c5a9751beac152d4e3a4e7899a490c819695 Mon Sep 17 00:00:00 2001 From: Aurangzaib Umer Date: Sun, 8 Sep 2024 03:45:01 +0500 Subject: [PATCH 010/110] resolved feedback --- .../fhircore/engine/datastore}/ContentCache.kt | 8 ++++++-- .../fhircore/engine/task/FhirCarePlanGenerator.kt | 13 +++++++++++-- .../fhircore/quest/ui/login/LoginActivity.kt | 5 ++++- .../ui/questionnaire/QuestionnaireViewModel.kt | 14 ++++++++------ .../ui/questionnaire/QuestionnaireViewModelTest.kt | 2 +- 5 files changed, 30 insertions(+), 12 deletions(-) rename android/{quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire => engine/src/main/java/org/smartregister/fhircore/engine/datastore}/ContentCache.kt (88%) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/ContentCache.kt similarity index 88% rename from android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt rename to android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/ContentCache.kt index beefa869c3..135764e009 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/ContentCache.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/datastore/ContentCache.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.smartregister.fhircore.quest.ui.questionnaire +package org.smartregister.fhircore.engine.datastore import androidx.collection.LruCache import kotlinx.coroutines.Dispatchers @@ -39,5 +39,9 @@ object ContentCache { return cache[resourceId]?.also { Timber.i("ContentCache:getResource: $resourceId") } } - suspend fun invalidate() = withContext(Dispatchers.IO) { cache.evictAll() } + suspend fun invalidate() = + withContext(Dispatchers.IO) { + cache.evictAll() + Timber.i("ContentCache: clearing cache") + } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt index 8f1a0f1eeb..f633552de6 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt @@ -58,6 +58,7 @@ import org.smartregister.fhircore.engine.R import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig import org.smartregister.fhircore.engine.configuration.event.EventType import org.smartregister.fhircore.engine.data.local.DefaultRepository +import org.smartregister.fhircore.engine.datastore.ContentCache import org.smartregister.fhircore.engine.util.extension.addResourceParameter import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.encodeResourceToString @@ -214,8 +215,16 @@ constructor( } source.setParameter(Task.SP_PERIOD, period) source.setParameter(ActivityDefinition.SP_VERSION, IntegerType(index)) - // need to cache these SM too - val structureMap = fhirEngine.get(IdType(action.transform).idPart) + val structureMapId = IdType(action.transform).idPart + val structureMap = + ContentCache.getResource(ResourceType.StructureMap.name + "/" + structureMapId)?.let { + it as StructureMap + } + ?: run { + fhirEngine.get(structureMapId).also { + ContentCache.saveResource(structureMapId, it) + } + } structureMapUtilities.transform( transformSupportServices.simpleWorkerContext, source, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/login/LoginActivity.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/login/LoginActivity.kt index 966faf9bca..c833f2efdc 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/login/LoginActivity.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/login/LoginActivity.kt @@ -24,10 +24,13 @@ import androidx.activity.viewModels import androidx.annotation.VisibleForTesting import androidx.compose.material.ExperimentalMaterialApi import androidx.core.os.bundleOf +import androidx.lifecycle.viewModelScope import androidx.work.WorkManager import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject +import kotlinx.coroutines.launch import org.smartregister.fhircore.engine.data.remote.shared.TokenAuthenticator +import org.smartregister.fhircore.engine.datastore.ContentCache import org.smartregister.fhircore.engine.p2p.dao.P2PReceiverTransferDao import org.smartregister.fhircore.engine.p2p.dao.P2PSenderTransferDao import org.smartregister.fhircore.engine.sync.AppSyncWorker @@ -84,7 +87,7 @@ open class LoginActivity : BaseMultiLanguageActivity() { navigateToPinLogin(launchSetup = false) } } - + viewModelScope.launch { ContentCache.invalidate() } navigateToHome.observe(loginActivity) { launchHomeScreen -> if (launchHomeScreen) { downloadNowWorkflowConfigs() diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index a8c8a62dbd..0b8307d27c 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -70,6 +70,7 @@ import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig import org.smartregister.fhircore.engine.configuration.app.ApplicationConfiguration import org.smartregister.fhircore.engine.configuration.app.CodingSystemUsage import org.smartregister.fhircore.engine.data.local.DefaultRepository +import org.smartregister.fhircore.engine.datastore.ContentCache import org.smartregister.fhircore.engine.domain.model.ActionParameter import org.smartregister.fhircore.engine.domain.model.ActionParameterType import org.smartregister.fhircore.engine.domain.model.isEditable @@ -147,20 +148,21 @@ constructor( questionnaireConfig: QuestionnaireConfig, ): Questionnaire? { if (questionnaireConfig.id.isEmpty() || questionnaireConfig.id.isBlank()) return null - var questionnaire = + var result = ContentCache.getResource(ResourceType.Questionnaire.name + "/" + questionnaireConfig.id) ?.copy() - if (questionnaire == null) { - questionnaire = - defaultRepository.loadResource(questionnaireConfig.id)?.also { ques -> + if (result == null) { + result = + defaultRepository.loadResource(questionnaireConfig.id)?.also { questionnaire + -> ContentCache.saveResource( questionnaireConfig.id, - ques.copy(), + questionnaire.copy(), ) } } - return questionnaire as Questionnaire + return result as Questionnaire } /** diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 0500e6a6b6..6ea9e28f6c 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -647,7 +647,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { Assert.assertEquals( samplePatientRegisterQuestionnaire, - ContentCache.getResource(ResourceType.Questionnaire.name + "/" + questionnaireConfig.id) + ContentCache.getResource(ResourceType.Questionnaire.name + "/" + questionnaireConfig.id), ) Assert.assertNotNull(questionnaire) Assert.assertEquals(questionnaireConfig.id, questionnaire?.id?.extractLogicalIdUuid()) From 45e135f0bd3edf8161ba0060724d795bfb4036fa Mon Sep 17 00:00:00 2001 From: Aurangzaib Umer Date: Sun, 8 Sep 2024 04:22:59 +0500 Subject: [PATCH 011/110] spotess ran --- .../fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 0b8307d27c..44fd34ec0f 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -153,7 +153,7 @@ constructor( ?.copy() if (result == null) { result = - defaultRepository.loadResource(questionnaireConfig.id)?.also { questionnaire + defaultRepository.loadResource(questionnaireConfig.id)?.also { questionnaire, -> ContentCache.saveResource( questionnaireConfig.id, From 266dd65e35c5533cef1d1a1e0bb1eef858b43d92 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 31 Jul 2024 12:27:20 +0300 Subject: [PATCH 012/110] =?UTF-8?q?Upgrade=20FHIR=20SDK=20depenencies=20?= =?UTF-8?q?=E2=AC=86=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../engine/task/WorkflowCarePlanGenerator.kt | 7 ++- .../util/extension/ResourceExtension.kt | 17 -------- .../util/extension/ResourceExtensionTest.kt | 43 ------------------- android/gradle/libs.versions.toml | 25 +++++------ 4 files changed, 16 insertions(+), 76 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt index c40b1a092a..641edb2f42 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt @@ -41,7 +41,8 @@ import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.Task import org.hl7.fhir.r4.utils.FHIRPathEngine import org.opencds.cqf.fhir.cql.LibraryEngine -import org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor +import org.opencds.cqf.fhir.cr.plandefinition.PlanDefinitionProcessor +import org.opencds.cqf.fhir.utility.monad.Eithers import org.opencds.cqf.fhir.utility.r4.Parameters.part import org.smartregister.fhircore.engine.data.local.DefaultRepository import timber.log.Timber @@ -128,9 +129,7 @@ constructor( params.addParameter(part("%subject", subject)) return planDefProcessor.apply( - null, - null, - planDefinition, + Eithers.forRight3(planDefinition), "Patient/${subject.id}", null, null, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt index 9c08e1e1a4..e7281e8f27 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt @@ -20,7 +20,6 @@ import android.content.Context import ca.uhn.fhir.context.FhirContext import ca.uhn.fhir.parser.IParser import ca.uhn.fhir.rest.gclient.ReferenceClientParam -import com.google.android.fhir.datacapture.extensions.createQuestionnaireResponseItem import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.get import com.google.android.fhir.search.search @@ -187,22 +186,6 @@ fun JSONObject.updateFrom(updated: JSONObject) { keys.forEach { key -> updated.opt(key)?.run { put(key, this) } } } -fun QuestionnaireResponse.generateMissingItems(questionnaire: Questionnaire) = - questionnaire.item.generateMissingItems(this.item) - -fun List.generateMissingItems( - qrItems: MutableList, -) { - this.forEachIndexed { index, qItem -> - // generate complete hierarchy if response item missing otherwise check for nested items - if (qrItems.isEmpty() || (index < qrItems.size && qItem.linkId != qrItems[index].linkId)) { - qrItems.add(index, qItem.createQuestionnaireResponseItem()) - } else if (index < qrItems.size) { - qItem.item.generateMissingItems(qrItems[index].item) - } - } -} - /** * Set all questions that are not of type [Questionnaire.QuestionnaireItemType.GROUP] to readOnly if * [readOnly] is true. This also generates the correct FHIRPath population expression for each diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt index 1fb5145f05..2a11213eb1 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt @@ -18,9 +18,6 @@ package org.smartregister.fhircore.engine.util.extension import android.app.Application import androidx.test.core.app.ApplicationProvider -import ca.uhn.fhir.context.FhirContext -import ca.uhn.fhir.context.FhirVersionEnum -import ca.uhn.fhir.parser.IParser import com.google.android.fhir.datacapture.extensions.logicalId import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest @@ -691,46 +688,6 @@ class ResourceExtensionTest : RobolectricTest() { ) } - @Test - fun testGenerateMissingItemsFromQuestionnaireShouldNotThrowException() { - val patientRegistrationQuestionnaire = - "register-patient-missingitems/missingitem-questionnaire.json".readFile() - val patientRegistrationQuestionnaireResponse = - "register-patient-missingitems/missingitem-questionnaire-response.json".readFile() - val iParser: IParser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser() - val questionnaire = - iParser.parseResource(Questionnaire::class.java, patientRegistrationQuestionnaire) - val questionnaireResponse = - iParser.parseResource( - QuestionnaireResponse::class.java, - patientRegistrationQuestionnaireResponse, - ) - - questionnaire.item.generateMissingItems(questionnaireResponse.item) - - Assert.assertTrue(questionnaireResponse.item.size <= questionnaire.item.size) - } - - @Test - fun testGenerateMissingItemsFromQuestionnaireResponseShouldNotThrowException() { - val patientRegistrationQuestionnaire = - "register-patient-missingitems/missingitem-questionnaire.json".readFile() - val patientRegistrationQuestionnaireResponse = - "register-patient-missingitems/missingitem-questionnaire-response.json".readFile() - val iParser: IParser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser() - val questionnaire = - iParser.parseResource(Questionnaire::class.java, patientRegistrationQuestionnaire) - val questionnaireResponse = - iParser.parseResource( - QuestionnaireResponse::class.java, - patientRegistrationQuestionnaireResponse, - ) - - questionnaireResponse.generateMissingItems(questionnaire) - - Assert.assertTrue(questionnaireResponse.item.size <= questionnaire.item.size) - } - @Test fun `prepareQuestionsForReadingOrEditing should set readOnly to true when passed`() { val questionnaire = Questionnaire() diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 560ed26d7e..22fc0cdabc 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -17,32 +17,31 @@ converter-gson = "2.9.0" core-ktx = "1.13.1" core-testing = "2.2.0" coverallsGradlePlugin = "2.12.2" -cqfFhirCr = "3.0.0-PRE9" +cqfFhirCr = "3.8.0" dagger-hilt = "2.51" datastore = "1.1.1" desugar-jdk-libs = "2.0.4" -dokkaBase = "1.8.20" +dokkaBase = "1.9.20" easy-rules-jexl = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview12-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview11.3-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview13-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview12-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview10-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview11-SNAPSHOT" foundation = "1.6.8" -fragment-ktx = "1.8.1" +fragment-ktx = "1.8.2" glide = "4.16.0" gradle = "8.3.2" gson = "2.10.1" hilt = "1.2.0" jetbrains = "1.9.20" -jetbrains-kotlin-jvm="1.9.22" jjwt = "0.9.1" joda-time = "2.10.14" json = "20230618" -jsonPath = "2.8.0" +jsonPath = "2.9.0" junit = "1.2.1" junit-jupiter = "5.10.3" junit-ktx = "1.2.1" @@ -54,7 +53,8 @@ kt3k-coveralls-ver="2.12.0" ktlint = "0.50.0" kujaku-library = "0.10.5-SNAPSHOT" leakcanary-android = "2.10" -lifecycle= "2.8.3" +lifecycle= "2.8.4" +logback-android = "3.0.0" mapbox-sdk-turf = "4.8.0" material = "1.12.0" mlkit-barcode-scanning = "17.3.0" @@ -74,10 +74,10 @@ prettytime = "5.0.2.Final" retrofit = "2.9.0" retrofit-mock = "2.9.0" retrofit2-kotlinx-serialization-converter = "0.8.0" -robolectric = "4.10.3" +robolectric = "4.13" rules = "1.6.1" security-crypto = "1.1.0-alpha06" -slf4j-nop = "1.7.36" +slf4j-nop = "2.0.7" spotlessPluginGradle = "6.25.0" stax-api = "1.0-2" timber = "5.0.1" @@ -159,6 +159,7 @@ leakcanary-android = { group = "com.squareup.leakcanary", name = "leakcanary-and lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" } lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" } +logback-android = { module = "com.github.tony19:logback-android", version.ref = "logback-android" } mapbox-sdk-turf = { group = "com.mapbox.mapboxsdk", name = "mapbox-sdk-turf", version.ref = "mapbox-sdk-turf" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } mlkit-barcode-scanning = { group = "com.google.mlkit", name = "barcode-scanning", version.ref = "mlkit-barcode-scanning"} @@ -209,7 +210,7 @@ dagger-hilt-android= { id = "com.google.dagger.hilt.android", version.ref = "dag kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-serialization" } kt3k-coveralls = { id = "com.github.kt3k.coveralls", version.ref = "kt3k-coveralls-ver" } org-jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "jetbrains" } -org-jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrains-kotlin-jvm" } +org-jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } org-owasp-dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "owasp" } [bundles] From 6522d71e3639cfccdf256f5192cacfc6151ea9b5 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Thu, 8 Aug 2024 18:57:59 +0300 Subject: [PATCH 013/110] Upragde SDC library --- android/gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 22fc0cdabc..78b7214f88 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -27,7 +27,7 @@ espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview13-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview13.1-SNAPSHOT" fhir-sdk-engine = "1.0.0-preview12-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" fhir-sdk-workflow = "0.1.0-alpha04-preview11-SNAPSHOT" From 241e03e76501f276ded3d36314e919481aa0c139 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 9 Aug 2024 16:04:15 +0300 Subject: [PATCH 014/110] Replace JWT token parser library --- android/engine/build.gradle.kts | 4 ++-- .../data/remote/shared/TokenAuthenticator.kt | 15 +++++++-------- .../engine/auth/TokenAuthenticatorTest.kt | 4 ++-- android/gradle/libs.versions.toml | 10 +++++----- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index 7393796830..f70ab00aa9 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { `jacoco-report` - `ktlint` + ktlint id("com.android.library") id("kotlin-android") id("kotlin-kapt") @@ -162,7 +162,7 @@ dependencies { api(libs.glide) api(libs.knowledge) { exclude(group = "org.slf4j", module = "jcl-over-slf4j") } api(libs.p2p.lib) - api(libs.jjwt) + api(libs.java.jwt) api(libs.fhir.common.utils) { exclude(group = "org.slf4j", module = "jcl-over-slf4j") } api(libs.runtime.livedata) api(libs.foundation) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt index 0f82f47883..ab3d7f305c 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt @@ -28,11 +28,12 @@ import android.os.Handler import android.os.Looper import android.os.Message import androidx.core.os.bundleOf +import com.auth0.jwt.JWT +import com.auth0.jwt.exceptions.JWTDecodeException +import com.auth0.jwt.interfaces.DecodedJWT import com.google.android.fhir.sync.HttpAuthenticationMethod import com.google.android.fhir.sync.HttpAuthenticator as FhirAuthenticator import dagger.hilt.android.qualifiers.ApplicationContext -import io.jsonwebtoken.JwtException -import io.jsonwebtoken.Jwts import java.io.IOException import java.net.UnknownHostException import java.util.Base64 @@ -62,7 +63,6 @@ constructor( @ApplicationContext val context: Context, ) : FhirAuthenticator { - private val jwtParser = Jwts.parser() private val authConfiguration by lazy { configService.provideAuthConfiguration() } private var isLoginPageRendered = false @@ -131,12 +131,11 @@ constructor( /** This function checks if token is null or empty or expired */ fun isTokenActive(authToken: String?): Boolean { if (authToken.isNullOrEmpty()) return false - val tokenPart = authToken.substringBeforeLast('.').plus(".") return try { - val body = jwtParser.parseClaimsJwt(tokenPart).body - body.expiration.after(today()) - } catch (jwtException: JwtException) { - false + val jwt: DecodedJWT? = JWT.decode(authToken) + jwt?.expiresAt!!.after(today()) + } catch (e: JWTDecodeException) { + return false } } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/auth/TokenAuthenticatorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/auth/TokenAuthenticatorTest.kt index 1aee73852b..35a3bcc403 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/auth/TokenAuthenticatorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/auth/TokenAuthenticatorTest.kt @@ -24,10 +24,10 @@ import android.accounts.OperationCanceledException import android.os.Bundle import androidx.core.os.bundleOf import androidx.test.core.app.ApplicationProvider +import com.auth0.jwt.exceptions.JWTDecodeException import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltTestApplication -import io.jsonwebtoken.JwtException import io.mockk.coEvery import io.mockk.every import io.mockk.just @@ -108,7 +108,7 @@ class TokenAuthenticatorTest : RobolectricTest() { } @Test - @Throws(JwtException::class) + @Throws(JWTDecodeException::class) fun testIsTokenActiveWithExpiredJwtToken() { Assert.assertFalse(tokenAuthenticator.isTokenActive("expired-token")) } diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 78b7214f88..fb3ad5ab0c 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -3,8 +3,8 @@ accompanist = "0.23.1" activity-compose = "1.8.2" androidJunit5 = "1.8.2.1" androidx-camera = "1.4.0-rc01" -androidx-paging = "3.3.0" -androidx-test = "1.6.1" +androidx-paging = "3.3.2" +androidx-test= "1.6.1" appcompat = "1.7.0" benchmark-junit = "1.2.4" cardview = "1.0.0" @@ -37,8 +37,8 @@ glide = "4.16.0" gradle = "8.3.2" gson = "2.10.1" hilt = "1.2.0" +java-jwt = "4.4.0" jetbrains = "1.9.20" -jjwt = "0.9.1" joda-time = "2.10.14" json = "20230618" jsonPath = "2.9.0" @@ -83,7 +83,7 @@ stax-api = "1.0-2" timber = "5.0.1" ui = "1.6.3" uiautomator = "2.3.0" -work = "2.9.0" +work = "2.9.1" xercesImpl = "2.12.2" [libraries] @@ -134,7 +134,7 @@ gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hilt" } hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hilt" } -jjwt = { group = "io.jsonwebtoken", name = "jjwt", version.ref = "jjwt" } +java-jwt = { module = "com.auth0:java-jwt", version.ref = "java-jwt" } joda-time = { group = "joda-time", name = "joda-time", version.ref = "joda-time" } json = { group = "org.json", name = "json", version.ref = "json" } json-path = { module = "com.jayway.jsonpath:json-path", version.ref = "jsonPath" } From acf6d23878d780f9ab6c3f5a3fcc1e458fdb43f6 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 9 Aug 2024 19:14:18 +0300 Subject: [PATCH 015/110] Fix CQL Content Test --- android/quest/build.gradle.kts | 1 + .../org/smartregister/fhircore/quest/CqlContentTest.kt | 7 +++++-- .../fhircore/quest/robolectric/RobolectricTest.kt | 10 ++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/android/quest/build.gradle.kts b/android/quest/build.gradle.kts index bf7ca9de70..ca90d1471f 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -450,6 +450,7 @@ dependencies { testImplementation(libs.navigation.testing) testImplementation(libs.kotlin.test) testImplementation(libs.work.testing) + testImplementation("org.skyscreamer:jsonassert:1.5.3") // To run only on debug builds debugImplementation(libs.ui.test.manifest) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index d9724ab580..7705221fad 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -24,6 +24,7 @@ import dagger.hilt.android.testing.HiltAndroidTest import java.io.File import javax.inject.Inject import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.Condition import org.hl7.fhir.r4.model.Library import org.hl7.fhir.r4.model.Parameters import org.hl7.fhir.r4.model.Resource @@ -32,6 +33,8 @@ import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test +import org.skyscreamer.jsonassert.JSONAssert +import org.skyscreamer.jsonassert.JSONCompareMode import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.valueToString @@ -233,8 +236,8 @@ class CqlContentTest : RobolectricTest() { .replaceTimePart() println(cqlResultStr) - println(expectedResource as String) + println(expectedResource) - Assert.assertEquals(expectedResource, cqlResultStr) + JSONAssert.assertEquals(expectedResource, cqlResultStr, JSONCompareMode.STRICT) } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt index 2e3a93709a..7ef8fa6368 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt @@ -106,7 +106,7 @@ abstract class RobolectricTest { .replace("#NOW", DateTimeType.now().valueAsString) .let { FhirContext.forCached(FhirVersionEnum.R4).newJsonParser().parseResource(it) } - fun IBaseResource.convertToString(trimTime: Boolean) = + fun IBaseResource.convertToString(trimTime: Boolean): String = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser().encodeResourceToString(this).let { // replace time part 11:11:11+05:00 with xx:xx:xx+xx:xx if (trimTime) { @@ -119,8 +119,9 @@ abstract class RobolectricTest { fun String.replaceTimePart() = // replace time part 11:11:11+05:00 with xx:xx:xx+xx:xx // replace time part 11:11:11.111+05:00 with xx:xx:xx+xx:xx - this.replace(Regex("\\d{2}:\\d{2}:\\d{2}.\\d{2}:\\d{2}"), "xx:xx:xx+xx:xx") - .replace(Regex("\\d{2}:\\d{2}:\\d{2}.\\d{3}.\\d{2}:\\d{2}"), "xx:xx:xx+xx:xx") + // replace time part 18:33:04.520481+03:00 + this.replace(Regex("\\d{2}:\\d{2}:\\d{2}.\\d[0-9,+]+:\\d{2}"), "xx:xx:xx+xx:xx") + .replace(Regex("\\d{2}:\\d{2}:\\d{2}.\\d{3}.\\d[0-9,+]+:\\d{2}"), "xx:xx:xx+xx:xx") fun buildStructureMapUtils(): StructureMapUtilities { val pcm = FilesystemPackageCacheManager(true) @@ -136,7 +137,8 @@ abstract class RobolectricTest { return StructureMapUtilities(contextR4, transformSupportServices) } - fun StructureMapUtilities.worker(): IWorkerContext = ReflectionHelpers.getField(this, "worker") + private fun StructureMapUtilities.worker(): IWorkerContext = + ReflectionHelpers.getField(this, "worker") fun transform( scu: StructureMapUtilities, From b6b45ff998ad982355a65690b961a8d31014efb8 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 13 Aug 2024 10:22:03 +0300 Subject: [PATCH 016/110] =?UTF-8?q?Fix=20unit=20tests=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/measure/MeasureReportViewModel.kt | 4 ++- .../quest/data/QuestXFhirQueryResolverTest.kt | 2 +- .../QuestionnaireActivityTest.kt | 26 ++++++++++--------- .../measure/MeasureReportViewModelTest.kt | 20 +++++++++++++- .../util/extensions/ConfigExtensionsKtTest.kt | 9 ++++--- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt index 29b192dcd0..b44219ffd4 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt @@ -343,8 +343,10 @@ constructor( } } + val measureReportPopulationResultList = + formatPopulationMeasureReports(result, reportConfigurations) _measureReportPopulationResultList.addAll( - formatPopulationMeasureReports(result, reportConfigurations), + measureReportPopulationResultList, ) } .onSuccess { diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/QuestXFhirQueryResolverTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/QuestXFhirQueryResolverTest.kt index cade896cb2..94390c95a8 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/QuestXFhirQueryResolverTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/QuestXFhirQueryResolverTest.kt @@ -47,7 +47,7 @@ class QuestXFhirQueryResolverTest : RobolectricTest() { @Test fun testQuestXFhirQueryResolver() = runTest(timeout = 120.seconds) { - val patient = Patient() + val patient = Patient().apply { setActive(true) } val task = Task() fhirEngine.create(patient, task) val xFhirResolver = QuestXFhirQueryResolver(fhirEngine) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt index 5d36f3a84e..e963a2fdf2 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt @@ -45,7 +45,7 @@ import junit.framework.TestCase.assertTrue import kotlin.test.assertNotNull import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Enumerations @@ -70,6 +70,7 @@ import org.smartregister.fhircore.engine.domain.model.ActionParameterType import org.smartregister.fhircore.engine.domain.model.RuleConfig import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.decodeResourceFromString +import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.quest.R import org.smartregister.fhircore.quest.app.fakes.Faker import org.smartregister.fhircore.quest.robolectric.RobolectricTest @@ -184,18 +185,20 @@ class QuestionnaireActivityTest : RobolectricTest() { setupActivity() Assert.assertTrue(questionnaireActivity.supportFragmentManager.fragments.isNotEmpty()) - val firstFragment = questionnaireActivity.supportFragmentManager.fragments.firstOrNull() + val firstFragment = + questionnaireActivity.supportFragmentManager.fragments[ + questionnaireActivity.supportFragmentManager.fragments.size - 1, + ] Assert.assertTrue(firstFragment is QuestionnaireFragment) // Questionnaire should be the same val fragmentQuestionnaire = - questionnaireActivity.supportFragmentManager.fragments - .firstOrNull() + firstFragment ?.arguments ?.getString("questionnaire") ?.decodeResourceFromString() - Assert.assertEquals(questionnaire.id, fragmentQuestionnaire?.id) + Assert.assertEquals(questionnaire.id, fragmentQuestionnaire?.id!!.extractLogicalIdUuid()) val sortedQuestionnaireItemLinkIds = questionnaire.item.map { it.linkId }.sorted().joinToString(",") val sortedFragmentQuestionnaireItemLinkIds = @@ -205,13 +208,12 @@ class QuestionnaireActivityTest : RobolectricTest() { } @Test - fun testThatOnBackPressShowsConfirmationAlertDialog() = - runTest(UnconfinedTestDispatcher()) { - setupActivity() - questionnaireActivity.onBackPressedDispatcher.onBackPressed() - val dialog = shadowOf(ShadowAlertDialog.getLatestAlertDialog()) - Assert.assertNotNull(dialog) - } + fun testThatOnBackPressShowsConfirmationAlertDialog() = runBlocking { + setupActivity() + questionnaireActivity.onBackPressedDispatcher.onBackPressed() + val dialog = shadowOf(ShadowAlertDialog.getLatestAlertDialog()) + Assert.assertNotNull(dialog) + } @Test fun `setupLocationServices should open location settings if location is disabled`() { diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt index 5cb739577d..886c4d3544 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt @@ -47,6 +47,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNotNull import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -90,6 +91,7 @@ import org.smartregister.fhircore.quest.data.report.measure.MeasureReportPagingS import org.smartregister.fhircore.quest.data.report.measure.MeasureReportRepository import org.smartregister.fhircore.quest.navigation.MeasureReportNavigationScreen import org.smartregister.fhircore.quest.robolectric.RobolectricTest +import org.smartregister.fhircore.quest.ui.report.measure.models.MeasureReportPopulationResult import org.smartregister.fhircore.quest.ui.shared.models.MeasureReportSubjectViewData import org.smartregister.fhircore.quest.util.mappers.MeasureReportSubjectViewDataMapper @@ -146,6 +148,8 @@ class MeasureReportViewModelTest : RobolectricTest() { measureReportRepository = measureReportRepository, ), ) + + every { measureReportViewModel.dispatcherProvider.io() } returns Dispatchers.IO } @Test @@ -326,7 +330,7 @@ class MeasureReportViewModelTest : RobolectricTest() { ) coEvery { measureReportViewModel.formatPopulationMeasureReports(any(), any()) } returns - emptyList() + listOf(MeasureReportPopulationResult()) coEvery { fhirEngine.retrievePreviouslyGeneratedMeasureReports( @@ -337,6 +341,20 @@ class MeasureReportViewModelTest : RobolectricTest() { ) } returns listOf(testMeasureReport) + coEvery { + measureReportRepository.evaluatePopulationMeasure( + startDateFormatted = any(), + endDateFormatted = any(), + measureUrl = any(), + subjects = any(), + existing = any(), + practitionerId = any(), + ) + } returns listOf(testMeasureReport) + + coEvery { measureReportRepository.fetchSubjects(any(ReportConfiguration::class)) } returns + listOf() + measureReportViewModel.reportTypeSelectorUiState.value = ReportTypeSelectorUiState(startDate = "21 Jan, 2022", endDate = "27 Jan, 2022") measureReportViewModel.reportConfigurations.add(reportConfiguration) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt index df3750e8f3..55d6071f99 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt @@ -701,15 +701,16 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Test fun testImageBitmapUpdatedCorrectlyGivenProfileConfiguration(): Unit = runTest { defaultRepository.create(addResourceTags = true, binaryImage) - val decodedImageMap = mutableStateMapOf() loadRemoteImagesBitmaps( profileConfiguration.views, registerRepository = registerRepository, computedValuesMap = emptyMap(), configurationRegistry.decodedImageMap, ) - Assert.assertTrue(decodedImageMap.isNotEmpty()) - Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) + Assert.assertTrue(configurationRegistry.decodedImageMap.isNotEmpty()) + Assert.assertTrue( + configurationRegistry.decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077"), + ) } @Test @@ -806,7 +807,7 @@ class ConfigExtensionsKtTest : RobolectricTest() { Assert.assertTrue(!decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) } - @Test(expected = Exception::class) + @Test fun testExceptionCaughtOnDecodingBitmap() = runTest { val cardViewProperties = profileConfiguration.views[0] as CardViewProperties val listViewProperties = cardViewProperties.content[0] as ListProperties From 8638f22ca85cf52ce730b202b86cac43b1f4b90f Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 13 Aug 2024 11:47:24 +0300 Subject: [PATCH 017/110] Refactor to remove Dispatch Provider --- .../engine/data/local/DefaultRepository.kt | 322 ++++++++---------- .../data/local/register/RegisterRepository.kt | 68 ++-- .../fhircore/engine/FhirExtractionTest.kt | 2 - .../data/local/DefaultRepositoryTest.kt | 6 - .../local/register/RegisterRepositoryTest.kt | 4 - .../engine/task/FhirCarePlanGeneratorTest.kt | 4 - .../task/FhirResourceExpireWorkerTest.kt | 3 +- .../report/measure/MeasureReportRepository.kt | 42 +-- .../geowidget/GeoWidgetLauncherViewModel.kt | 2 - .../fhircore/quest/data/DataMigrationTest.kt | 26 +- .../measure/MeasureReportPagingSourceTest.kt | 2 - .../measure/MeasureReportRepositoryTest.kt | 3 - .../GeoWidgetLauncherViewModelTest.kt | 4 - .../quest/ui/profile/ProfileViewModelTest.kt | 2 - .../QuestionnaireActivityTest.kt | 4 - .../QuestionnaireViewModelTest.kt | 3 +- 16 files changed, 220 insertions(+), 277 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 85b6059587..2bcddbd54d 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -46,7 +46,6 @@ import java.util.LinkedList import java.util.UUID import javax.inject.Inject import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull @@ -83,7 +82,6 @@ import org.smartregister.fhircore.engine.domain.model.ResourceFilterExpression import org.smartregister.fhircore.engine.domain.model.RuleConfig import org.smartregister.fhircore.engine.domain.model.SortConfig import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.encodeResourceToString @@ -103,7 +101,6 @@ open class DefaultRepository @Inject constructor( open val fhirEngine: FhirEngine, - open val dispatcherProvider: DispatcherProvider, open val sharedPreferencesHelper: SharedPreferencesHelper, open val configurationRegistry: ConfigurationRegistry, open val configService: ConfigService, @@ -114,17 +111,15 @@ constructor( ) { suspend inline fun loadResource(resourceId: String): T? { - return withContext(dispatcherProvider.io()) { fhirEngine.loadResource(resourceId) } + return fhirEngine.loadResource(resourceId) } suspend fun loadResource(resourceId: String, resourceType: ResourceType): Resource = - withContext(dispatcherProvider.io()) { fhirEngine.get(resourceType, resourceId) } + fhirEngine.get(resourceType, resourceId) suspend fun loadResource(reference: Reference) = - withContext(dispatcherProvider.io()) { - IdType(reference.reference).let { - fhirEngine.get(ResourceType.fromCode(it.resourceType), it.idPart) - } + IdType(reference.reference).let { + fhirEngine.get(ResourceType.fromCode(it.resourceType), it.idPart) } suspend inline fun searchResourceFor( @@ -134,19 +129,17 @@ constructor( dataQueries: List = listOf(), configComputedRuleValues: Map, ): List = - withContext(dispatcherProvider.io()) { - fhirEngine - .search { - filterByResourceTypeId(token, subjectType, subjectId) - dataQueries.forEach { - filterBy( - dataQuery = it, - configComputedRuleValues = configComputedRuleValues, - ) - } + fhirEngine + .search { + filterByResourceTypeId(token, subjectType, subjectId) + dataQueries.forEach { + filterBy( + dataQuery = it, + configComputedRuleValues = configComputedRuleValues, + ) } - .map { it.resource } - } + } + .map { it.resource } suspend inline fun search(search: Search) = fhirEngine.search(search).map { it.resource } @@ -161,17 +154,13 @@ constructor( * param [addResourceTags] */ suspend fun create(addResourceTags: Boolean = true, vararg resource: Resource): List { - return withContext(dispatcherProvider.io()) { - preProcessResources(addResourceTags, *resource) - fhirEngine.create(*resource) - } + preProcessResources(addResourceTags, *resource) + return fhirEngine.create(*resource) } suspend fun createRemote(addResourceTags: Boolean = true, vararg resource: Resource) { - return withContext(dispatcherProvider.io()) { - preProcessResources(addResourceTags, *resource) - fhirEngine.create(*resource, isLocalOnly = true) - } + preProcessResources(addResourceTags, *resource) + fhirEngine.create(*resource, isLocalOnly = true) } private fun preProcessResources(addResourceTags: Boolean, vararg resource: Resource) { @@ -197,23 +186,19 @@ constructor( resourceId: String, softDelete: Boolean = false, ) { - withContext(dispatcherProvider.io()) { - if (softDelete) { - val resource = fhirEngine.get(resourceType, resourceId) - softDelete(resource) - } else { - fhirEngine.delete(resourceType, resourceId) - } + if (softDelete) { + val resource = fhirEngine.get(resourceType, resourceId) + softDelete(resource) + } else { + fhirEngine.delete(resourceType, resourceId) } } suspend fun delete(resource: Resource, softDelete: Boolean = false) { - withContext(dispatcherProvider.io()) { - if (softDelete) { - softDelete(resource) - } else { - fhirEngine.delete(resource.resourceType, resource.logicalId) - } + if (softDelete) { + softDelete(resource) + } else { + fhirEngine.delete(resource.resourceType, resource.logicalId) } } @@ -242,24 +227,20 @@ constructor( * param [addMandatoryTags] */ suspend fun addOrUpdate(addMandatoryTags: Boolean = true, resource: R) { - return withContext(dispatcherProvider.io()) { - resource.updateLastUpdated() - try { - fhirEngine.get(resource.resourceType, resource.logicalId).run { - val updateFrom = updateFrom(resource) - fhirEngine.update(updateFrom) - } - } catch (resourceNotFoundException: ResourceNotFoundException) { - create(addMandatoryTags, resource) + resource.updateLastUpdated() + try { + fhirEngine.get(resource.resourceType, resource.logicalId).run { + val updateFrom = updateFrom(resource) + fhirEngine.update(updateFrom) } + } catch (resourceNotFoundException: ResourceNotFoundException) { + create(addMandatoryTags, resource) } } suspend fun update(resource: R) { - return withContext(dispatcherProvider.io()) { - resource.updateLastUpdated() - fhirEngine.update(resource) - } + resource.updateLastUpdated() + fhirEngine.update(resource) } suspend fun loadManagingEntity(group: Group) = @@ -557,7 +538,7 @@ constructor( }, ): Long = kotlin - .runCatching { withContext(dispatcherProvider.io()) { fhirEngine.count(this@count) } } + .runCatching { fhirEngine.count(this@count) } .onSuccess { count -> onSuccess(count) } .onFailure { throwable -> onFailure(throwable) } .getOrDefault(0) @@ -765,63 +746,60 @@ constructor( subject: Resource, eventWorkflow: EventWorkflow, ) { - withContext(dispatcherProvider.io()) { - val configRules = configRulesExecutor.generateRules(resourceConfig.configRules ?: listOf()) - val computedValuesMap = - configRulesExecutor.fireRules(rules = configRules, baseResource = subject).mapValues { - entry, - -> - val initialValue = entry.value.toString() - if (initialValue.contains('/')) { - """${initialValue.substringBefore("/")}/${initialValue.extractLogicalIdUuid()}""" - } else { - initialValue - } - } - - Timber.i("Computed values map = ${computedValuesMap.values}") - val search = - Search(resourceConfig.resource).apply { - applyConfiguredSortAndFilters( - resourceConfig = resourceConfig, - sortData = false, - filterActiveResources = null, - configComputedRuleValues = computedValuesMap, - ) + val configRules = configRulesExecutor.generateRules(resourceConfig.configRules ?: listOf()) + val computedValuesMap = + configRulesExecutor.fireRules(rules = configRules, baseResource = subject).mapValues { entry, + -> + val initialValue = entry.value.toString() + if (initialValue.contains('/')) { + """${initialValue.substringBefore("/")}/${initialValue.extractLogicalIdUuid()}""" + } else { + initialValue } - val resources = fhirEngine.search(search).map { it.resource } - val filteredResources = - filterResourcesByFhirPathExpression( - resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, - resources = resources, - ) - filteredResources.forEach { - Timber.i("Closing Resource type ${it.resourceType.name} and id ${it.id}") - closeResource(resource = it, eventWorkflow = eventWorkflow) } - val retrievedRelatedResources = - retrieveRelatedResources( - resources = resources, - relatedResourcesConfigs = resourceConfig.relatedResources, - relatedResourceWrapper = RelatedResourceWrapper(), + Timber.i("Computed values map = ${computedValuesMap.values}") + val search = + Search(resourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = resourceConfig, + sortData = false, + filterActiveResources = null, configComputedRuleValues = computedValuesMap, ) + } + val resources = fhirEngine.search(search).map { it.resource } + val filteredResources = + filterResourcesByFhirPathExpression( + resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, + resources = resources, + ) + filteredResources.forEach { + Timber.i("Closing Resource type ${it.resourceType.name} and id ${it.id}") + closeResource(resource = it, eventWorkflow = eventWorkflow) + } - retrievedRelatedResources.relatedResourceMap.forEach { resourcesMap -> - val filteredRelatedResources = - filterResourcesByFhirPathExpression( - resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, - resources = resourcesMap.value, - ) + val retrievedRelatedResources = + retrieveRelatedResources( + resources = resources, + relatedResourcesConfigs = resourceConfig.relatedResources, + relatedResourceWrapper = RelatedResourceWrapper(), + configComputedRuleValues = computedValuesMap, + ) - filteredRelatedResources.forEach { resource -> - Timber.i( - "Closing related Resource type ${resource.resourceType.name} and id ${resource.id}", - ) - if (filterRelatedResource(resource, resourceConfig)) { - closeResource(resource = resource, eventWorkflow = eventWorkflow) - } + retrievedRelatedResources.relatedResourceMap.forEach { resourcesMap -> + val filteredRelatedResources = + filterResourcesByFhirPathExpression( + resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, + resources = resourcesMap.value, + ) + + filteredRelatedResources.forEach { resource -> + Timber.i( + "Closing related Resource type ${resource.resourceType.name} and id ${resource.id}", + ) + if (filterRelatedResource(resource, resourceConfig)) { + closeResource(resource = resource, eventWorkflow = eventWorkflow) } } } @@ -917,7 +895,7 @@ constructor( val updatedResource = parser.parseResource(resourceDefinition, updatedResourceDocument.jsonString()) updatedResource.setId(updatedResource.idElement.idPart) - withContext(dispatcherProvider.io()) { fhirEngine.update(updatedResource as Resource) } + fhirEngine.update(updatedResource as Resource) } private fun getJsonContent(jsonElement: JsonElement): Any? { @@ -948,9 +926,7 @@ constructor( suspend fun purge(resource: Resource, forcePurge: Boolean) { try { - withContext(dispatcherProvider.io()) { - fhirEngine.purge(resource.resourceType, resource.logicalId, forcePurge) - } + fhirEngine.purge(resource.resourceType, resource.logicalId, forcePurge) } catch (resourceNotFoundException: ResourceNotFoundException) { Timber.e( "Purge failed -> Resource with ID ${resource.logicalId} does not exist", @@ -968,64 +944,62 @@ constructor( pageSize: Int? = null, configRules: List?, ): List { - return withContext(dispatcherProvider.io()) { - val baseResourceConfig = fhirResourceConfig.baseResource - val relatedResourcesConfig = fhirResourceConfig.relatedResources - val configComputedRuleValues = configRules.configRulesComputedValues() - val search = - Search(type = baseResourceConfig.resource).apply { - applyConfiguredSortAndFilters( - resourceConfig = baseResourceConfig, - filterActiveResources = filterActiveResources, - sortData = true, - configComputedRuleValues = configComputedRuleValues, + val baseResourceConfig = fhirResourceConfig.baseResource + val relatedResourcesConfig = fhirResourceConfig.relatedResources + val configComputedRuleValues = configRules.configRulesComputedValues() + val search = + Search(type = baseResourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = baseResourceConfig, + filterActiveResources = filterActiveResources, + sortData = true, + configComputedRuleValues = configComputedRuleValues, + ) + applyFilterByRelatedEntityLocationMetaTag( + baseResourceConfig.resource, + filterByRelatedEntityLocationMetaTag, + ) + if (currentPage != null && pageSize != null) { + count = pageSize + from = currentPage * pageSize + } + } + + val baseFhirResources = + kotlin + .runCatching { + val searchTime = System.currentTimeMillis() + val result = fhirEngine.search(search) + Timber.w( + "It took ${(System.currentTimeMillis() - searchTime) / 1000} second(s) to search resources of type ${baseResourceConfig.resource}", ) - applyFilterByRelatedEntityLocationMetaTag( - baseResourceConfig.resource, - filterByRelatedEntityLocationMetaTag, + result + } + .onFailure { + Timber.e( + it, + "Error retrieving resources. Empty list returned by default", ) - if (currentPage != null && pageSize != null) { - count = pageSize - from = currentPage * pageSize - } } + .getOrDefault(emptyList()) - val baseFhirResources = - kotlin - .runCatching { - val searchTime = System.currentTimeMillis() - val result = fhirEngine.search(search) - Timber.w( - "It took ${(System.currentTimeMillis() - searchTime) / 1000} second(s) to search resources of type ${baseResourceConfig.resource}", - ) - result - } - .onFailure { - Timber.e( - it, - "Error retrieving resources. Empty list returned by default", - ) - } - .getOrDefault(emptyList()) - - baseFhirResources.map { searchResult -> - val retrievedRelatedResources = - retrieveRelatedResources( - resources = listOf(searchResult.resource), - relatedResourcesConfigs = relatedResourcesConfig, - relatedResourceWrapper = RelatedResourceWrapper(), - configComputedRuleValues = configComputedRuleValues, - ) - val secondaryRepositoryResourceData = - secondaryResourceConfigs.retrieveSecondaryRepositoryResourceData(filterActiveResources) - RepositoryResourceData( - resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, - resource = searchResult.resource, - relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, - relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, - secondaryRepositoryResourceData = secondaryRepositoryResourceData, + return baseFhirResources.map { searchResult -> + val retrievedRelatedResources = + retrieveRelatedResources( + resources = listOf(searchResult.resource), + relatedResourcesConfigs = relatedResourcesConfig, + relatedResourceWrapper = RelatedResourceWrapper(), + configComputedRuleValues = configComputedRuleValues, ) - } + val secondaryRepositoryResourceData = + secondaryResourceConfigs.retrieveSecondaryRepositoryResourceData(filterActiveResources) + RepositoryResourceData( + resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, + resource = searchResult.resource, + relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, + relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, + secondaryRepositoryResourceData = secondaryRepositoryResourceData, + ) } } @@ -1159,18 +1133,16 @@ constructor( } private suspend fun retrieveSubLocations(locationId: String) = - withContext(dispatcherProvider.io()) { - fhirEngine - .search( - Search(type = ResourceType.Location).apply { - filter( - Location.PARTOF, - { value = locationId.asReference(ResourceType.Location).reference }, - ) - }, - ) - .mapTo(LinkedList()) { it.resource } - } + fhirEngine + .search( + Search(type = ResourceType.Location).apply { + filter( + Location.PARTOF, + { value = locationId.asReference(ResourceType.Location).reference }, + ) + }, + ) + .mapTo(LinkedList()) { it.resource } /** * A wrapper data class to hold search results. All related resources are flattened into one Map diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt index d8ae465d39..71405b7344 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt @@ -22,7 +22,6 @@ import com.google.android.fhir.FhirEngine import com.google.android.fhir.search.Search import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject -import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.Resource import org.smartregister.fhircore.engine.configuration.ConfigType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry @@ -36,7 +35,6 @@ import org.smartregister.fhircore.engine.domain.model.FhirResourceConfig import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData import org.smartregister.fhircore.engine.domain.repository.Repository import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -46,7 +44,6 @@ class RegisterRepository @Inject constructor( override val fhirEngine: FhirEngine, - override val dispatcherProvider: DispatcherProvider, override val sharedPreferencesHelper: SharedPreferencesHelper, override val configurationRegistry: ConfigurationRegistry, override val configService: ConfigService, @@ -58,7 +55,6 @@ constructor( Repository, DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -124,44 +120,42 @@ constructor( fhirResourceConfig: FhirResourceConfig?, paramsList: Array?, ): RepositoryResourceData { - return withContext(dispatcherProvider.io()) { - val paramsMap: Map = - paramsList - ?.asSequence() - ?.filter { - (it.paramType == ActionParameterType.PARAMDATA || - it.paramType == ActionParameterType.UPDATE_DATE_ON_EDIT) && it.value.isNotEmpty() - } - ?.associate { it.key to it.value } ?: emptyMap() + val paramsMap: Map = + paramsList + ?.asSequence() + ?.filter { + (it.paramType == ActionParameterType.PARAMDATA || + it.paramType == ActionParameterType.UPDATE_DATE_ON_EDIT) && it.value.isNotEmpty() + } + ?.associate { it.key to it.value } ?: emptyMap() - val profileConfiguration = retrieveProfileConfiguration(profileId, paramsMap) - val resourceConfig = fhirResourceConfig ?: profileConfiguration.fhirResource - val baseResourceConfig = resourceConfig.baseResource + val profileConfiguration = retrieveProfileConfiguration(profileId, paramsMap) + val resourceConfig = fhirResourceConfig ?: profileConfiguration.fhirResource + val baseResourceConfig = resourceConfig.baseResource - val baseResource: Resource = - fhirEngine.get(baseResourceConfig.resource, resourceId.extractLogicalIdUuid()) + val baseResource: Resource = + fhirEngine.get(baseResourceConfig.resource, resourceId.extractLogicalIdUuid()) - val configComputedRuleValues = profileConfiguration.configRules.configRulesComputedValues() + val configComputedRuleValues = profileConfiguration.configRules.configRulesComputedValues() - val retrievedRelatedResources = - retrieveRelatedResources( - resources = listOf(baseResource), - relatedResourcesConfigs = resourceConfig.relatedResources, - relatedResourceWrapper = RelatedResourceWrapper(), - configComputedRuleValues = configComputedRuleValues, - ) - - RepositoryResourceData( - resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, - resource = baseResource, - relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, - relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, - secondaryRepositoryResourceData = - profileConfiguration.secondaryResources.retrieveSecondaryRepositoryResourceData( - profileConfiguration.filterActiveResources, - ), + val retrievedRelatedResources = + retrieveRelatedResources( + resources = listOf(baseResource), + relatedResourcesConfigs = resourceConfig.relatedResources, + relatedResourceWrapper = RelatedResourceWrapper(), + configComputedRuleValues = configComputedRuleValues, ) - } + + return RepositoryResourceData( + resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, + resource = baseResource, + relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, + relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, + secondaryRepositoryResourceData = + profileConfiguration.secondaryResources.retrieveSecondaryRepositoryResourceData( + profileConfiguration.filterActiveResources, + ), + ) } fun retrieveProfileConfiguration(profileId: String, paramsMap: Map) = diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/FhirExtractionTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/FhirExtractionTest.kt index 79cc9d7842..0b8d49b085 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/FhirExtractionTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/FhirExtractionTest.kt @@ -34,7 +34,6 @@ import javax.inject.Inject import junit.framework.Assert.assertEquals import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertTrue -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Encounter @@ -75,7 +74,6 @@ class FhirExtractionTest : RobolectricTest() { hiltRule.inject() structureMapUtilities = StructureMapUtilities(transformSupportServices.simpleWorkerContext) val workManager = mockk() - every { defaultRepository.dispatcherProvider.io() } returns Dispatchers.IO every { defaultRepository.fhirEngine } returns fhirEngine every { workManager.enqueue(any()) } returns mockk() } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt index d0ec498516..8b773d4c89 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt @@ -95,7 +95,6 @@ import org.smartregister.fhircore.engine.domain.model.ResourceFilterExpression import org.smartregister.fhircore.engine.domain.model.RuleConfig import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.formatDate @@ -126,19 +125,16 @@ class DefaultRepositoryTest : RobolectricTest() { private val application = ApplicationProvider.getApplicationContext() private val configurationRegistry: ConfigurationRegistry = Faker.buildTestConfigurationRegistry() private val context = ApplicationProvider.getApplicationContext() - private lateinit var dispatcherProvider: DefaultDispatcherProvider private lateinit var sharedPreferenceHelper: SharedPreferencesHelper private lateinit var defaultRepository: DefaultRepository @Before fun setUp() { hiltRule.inject() - dispatcherProvider = DefaultDispatcherProvider() sharedPreferenceHelper = SharedPreferencesHelper(application, gson) defaultRepository = DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferenceHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -551,7 +547,6 @@ class DefaultRepositoryTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = mockk(), configService = mockk(), @@ -629,7 +624,6 @@ class DefaultRepositoryTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = mockk(), configService = mockk(), diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt index 1dc0027224..2da2b9fbfd 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt @@ -67,7 +67,6 @@ import org.smartregister.fhircore.engine.domain.model.SyncLocationState import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rule.CoroutineTestRule import org.smartregister.fhircore.engine.rulesengine.RulesFactory -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.encodeJson import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -99,8 +98,6 @@ class RegisterRepositoryTest : RobolectricTest() { @Inject lateinit var fhirPathDataExtractor: FhirPathDataExtractor - @Inject lateinit var dispatcherProvider: DispatcherProvider - @Inject lateinit var fhirEngine: FhirEngine @Inject lateinit var parser: IParser @@ -115,7 +112,6 @@ class RegisterRepositoryTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt index 589a5494c6..fec08a518b 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt @@ -113,7 +113,6 @@ import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rule.CoroutineTestRule -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.REFERENCE import org.smartregister.fhircore.engine.util.extension.SDF_YYYY_MM_DD import org.smartregister.fhircore.engine.util.extension.asReference @@ -147,8 +146,6 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { @Inject lateinit var fhirEngine: FhirEngine - @Inject lateinit var testDispatcher: DispatcherProvider - @Inject lateinit var configurationRegistry: ConfigurationRegistry private val context: Context = ApplicationProvider.getApplicationContext() @@ -171,7 +168,6 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { fun setup() { hiltRule.inject() structureMapUtilities = StructureMapUtilities(transformSupportServices.simpleWorkerContext) - every { defaultRepository.dispatcherProvider } returns testDispatcher every { defaultRepository.fhirEngine } returns fhirEngine coEvery { defaultRepository.create(anyBoolean(), any()) } returns listOf() diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt index e6ce1dfe6b..1490a719f6 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt @@ -95,7 +95,7 @@ class FhirResourceExpireWorkerTest : RobolectricTest() { period = Period().apply { end = DateTime().plusDays(-2).toDate() } } } - val serviceRequest = + private val serviceRequest = ServiceRequest().apply { id = UUID.randomUUID().toString() status = ServiceRequest.ServiceRequestStatus.COMPLETED @@ -112,7 +112,6 @@ class FhirResourceExpireWorkerTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index a50b491869..110da18a5b 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -25,7 +25,6 @@ import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject -import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.Measure @@ -36,7 +35,6 @@ import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.configuration.report.measure.ReportConfiguration import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -47,7 +45,6 @@ class MeasureReportRepository @Inject constructor( override val fhirEngine: FhirEngine, - override val dispatcherProvider: DispatcherProvider, override val sharedPreferencesHelper: SharedPreferencesHelper, override val configurationRegistry: ConfigurationRegistry, override val configService: ConfigService, @@ -60,7 +57,6 @@ constructor( ) : DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -91,31 +87,29 @@ constructor( ): List { val measureReport = mutableListOf() try { - withContext(dispatcherProvider.io()) { - if (subjects.isNotEmpty()) { - subjects - .map { - runMeasureReport( - measureUrl = measureUrl, - reportType = MeasureReportViewModel.SUBJECT, - startDateFormatted = startDateFormatted, - endDateFormatted = endDateFormatted, - subject = it, - practitionerId = practitionerId, - ) - } - .forEach { subject -> measureReport.add(subject) } - } else { - runMeasureReport( + if (subjects.isNotEmpty()) { + subjects + .map { + runMeasureReport( measureUrl = measureUrl, - reportType = MeasureReportViewModel.POPULATION, + reportType = MeasureReportViewModel.SUBJECT, startDateFormatted = startDateFormatted, endDateFormatted = endDateFormatted, - subject = null, + subject = it, practitionerId = practitionerId, ) - .also { measureReport.add(it) } - } + } + .forEach { subject -> measureReport.add(subject) } + } else { + runMeasureReport( + measureUrl = measureUrl, + reportType = MeasureReportViewModel.POPULATION, + startDateFormatted = startDateFormatted, + endDateFormatted = endDateFormatted, + subject = null, + practitionerId = practitionerId, + ) + .also { measureReport.add(it) } } measureReport.forEach { report -> diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt index 9c4d8d056f..af86768fbc 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt @@ -40,7 +40,6 @@ import org.smartregister.fhircore.engine.domain.model.ActionParameterType import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData import org.smartregister.fhircore.engine.domain.model.SnackBarMessageConfig import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferenceKey import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid @@ -54,7 +53,6 @@ class GeoWidgetLauncherViewModel @Inject constructor( val defaultRepository: DefaultRepository, - val dispatcherProvider: DispatcherProvider, val sharedPreferencesHelper: SharedPreferencesHelper, val resourceDataRulesExecutor: ResourceDataRulesExecutor, val configurationRegistry: ConfigurationRegistry, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/DataMigrationTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/DataMigrationTest.kt index 1aafa14d4f..7586d323b6 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/DataMigrationTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/DataMigrationTest.kt @@ -77,6 +77,24 @@ class DataMigrationTest : RobolectricTest() { dataMigration.migrate( migrationConfigs = listOf( + MigrationConfig( + resourceConfig = + FhirResourceConfig( + baseResource = ResourceConfig(resource = ResourceType.Patient), + ), + version = 7, + rules = + listOf( + RuleConfig(name = "value", actions = listOf("data.put('value', 'female')")), + ), + updateValues = + listOf( + UpdateValueConfig( + jsonPathExpression = "\$.gender", + computedValueKey = "value", + ), + ), + ), MigrationConfig( resourceConfig = FhirResourceConfig( @@ -103,9 +121,9 @@ class DataMigrationTest : RobolectricTest() { Assert.assertTrue(updatedPatient?.gender != patient.gender) Assert.assertEquals(Enumerations.AdministrativeGender.FEMALE, updatedPatient?.gender) - // Version updated to 2 + // Version updated to 7 (the maximum migration version) Assert.assertEquals( - 2, + 7, preferenceDataStore.read(PreferenceDataStore.MIGRATION_VERSION).first(), ) } @@ -179,9 +197,9 @@ class DataMigrationTest : RobolectricTest() { Assert.assertNotNull(updatedTask?.basedOn) Assert.assertEquals("CarePlan/${carePlan.logicalId}", updatedTask?.basedOnFirstRep?.reference) - // Version updated to 2 + // Version updated to 1 Assert.assertEquals( - 2, + 1, preferenceDataStore.read(PreferenceDataStore.MIGRATION_VERSION).first(), ) } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt index e16149d5c8..b24e6d90ad 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt @@ -49,7 +49,6 @@ import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor import org.smartregister.fhircore.engine.rulesengine.RulesFactory import org.smartregister.fhircore.engine.rulesengine.services.LocationService -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import org.smartregister.fhircore.quest.app.fakes.Faker @@ -108,7 +107,6 @@ class MeasureReportPagingSourceTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = fhirEngine, - dispatcherProvider = DefaultDispatcherProvider(), sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt index 5dc3b41746..b0c43d33f4 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt @@ -49,7 +49,6 @@ import org.smartregister.fhircore.engine.data.local.register.RegisterRepository import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor import org.smartregister.fhircore.engine.rulesengine.RulesFactory import org.smartregister.fhircore.engine.rulesengine.services.LocationService -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.SDF_YYYY_MM_DD import org.smartregister.fhircore.engine.util.extension.firstDayOfMonth @@ -114,7 +113,6 @@ class MeasureReportRepositoryTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = fhirEngine, - dispatcherProvider = DefaultDispatcherProvider(), sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), @@ -128,7 +126,6 @@ class MeasureReportRepositoryTest : RobolectricTest() { measureReportRepository = MeasureReportRepository( fhirEngine = fhirEngine, - dispatcherProvider = DefaultDispatcherProvider(), sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt index e2a47a82cf..8b56a2dfa3 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt @@ -49,7 +49,6 @@ import org.smartregister.fhircore.engine.domain.model.FhirResourceConfig import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.domain.model.SnackBarMessageConfig import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.geowidget.model.GeoJsonFeature import org.smartregister.fhircore.quest.app.fakes.Faker @@ -63,8 +62,6 @@ class GeoWidgetLauncherViewModelTest : RobolectricTest() { @Inject lateinit var defaultRepository: DefaultRepository - @Inject lateinit var dispatcherProvider: DefaultDispatcherProvider - @Inject lateinit var sharedPreferencesHelper: SharedPreferencesHelper @Inject lateinit var resourceDataRulesExecutor: ResourceDataRulesExecutor @@ -102,7 +99,6 @@ class GeoWidgetLauncherViewModelTest : RobolectricTest() { viewModel = GeoWidgetLauncherViewModel( defaultRepository = defaultRepository, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, resourceDataRulesExecutor = resourceDataRulesExecutor, configurationRegistry = configurationRegistry, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt index 9c63ea2390..4990d13671 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt @@ -54,7 +54,6 @@ import org.smartregister.fhircore.engine.domain.model.OverflowMenuItemConfig import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData import org.smartregister.fhircore.engine.domain.model.ResourceData import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.BLACK_COLOR_HEX_CODE import org.smartregister.fhircore.engine.util.extension.getActivity @@ -99,7 +98,6 @@ class ProfileViewModelTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = mockk(), - dispatcherProvider = DefaultDispatcherProvider(), sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt index e963a2fdf2..2c80d74397 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt @@ -68,7 +68,6 @@ 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.RuleConfig -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.decodeResourceFromString import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.quest.R @@ -89,8 +88,6 @@ class QuestionnaireActivityTest : RobolectricTest() { private lateinit var questionnaireActivityController: ActivityController private lateinit var questionnaireActivity: QuestionnaireActivity - @Inject lateinit var testDispatcherProvider: DispatcherProvider - @BindValue lateinit var defaultRepository: DefaultRepository @BindValue @@ -104,7 +101,6 @@ class QuestionnaireActivityTest : RobolectricTest() { } defaultRepository = mockk(relaxUnitFun = true) { - every { dispatcherProvider } returns testDispatcherProvider every { fhirEngine } returns spyk(this@QuestionnaireActivityTest.fhirEngine) } questionnaireConfig = diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index d0c93b6fdc..2cc178efa2 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -176,7 +176,6 @@ class QuestionnaireViewModelTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -199,7 +198,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { spyk( QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), From da63a1b13ae37335cb4023e15b12d83fbed6b1e5 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 16 Aug 2024 12:40:25 +0300 Subject: [PATCH 018/110] =?UTF-8?q?Fix=20unit=20test=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fhircore/geowidget/screens/GeoWidgetViewModelTest.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt index b84dcd5928..baaf1e49cd 100644 --- a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt +++ b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt @@ -48,7 +48,6 @@ import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import org.smartregister.fhircore.geowidget.model.GeoJsonFeature import org.smartregister.fhircore.geowidget.model.Geometry import org.smartregister.fhircore.geowidget.model.ServicePointType -import org.smartregister.fhircore.geowidget.rule.CoroutineTestRule @RunWith(RobolectricTestRunner::class) @Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class) @@ -59,8 +58,6 @@ class GeoWidgetViewModelTest { @get:Rule(order = 1) var instantTaskExecutorRule = InstantTaskExecutorRule() - @get:Rule(order = 2) var coroutinesTestRule = CoroutineTestRule() - @Inject lateinit var configService: ConfigService private lateinit var configurationRegistry: ConfigurationRegistry @@ -93,7 +90,6 @@ class GeoWidgetViewModelTest { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = coroutinesTestRule.testDispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -103,7 +99,7 @@ class GeoWidgetViewModelTest { context = ApplicationProvider.getApplicationContext(), ), ) - geoWidgetViewModel = spyk(GeoWidgetViewModel(coroutinesTestRule.testDispatcherProvider)) + geoWidgetViewModel = spyk(GeoWidgetViewModel(dispatcherProvider)) coEvery { defaultRepository.create(any()) } returns emptyList() } From a520fea24e52f094d0fcced9d2ac65d7da59fbd7 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 19 Aug 2024 19:58:44 +0300 Subject: [PATCH 019/110] Refactor Knowledge Manager Resources Persistance --- .../configuration/ConfigurationRegistry.kt | 12 ++++++--- .../fhircore/engine/di/CoreModule.kt | 25 ++++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index 6dc039191f..5aebbe77ca 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -627,9 +627,14 @@ constructor( resource.idElement.idPart } - return File(context.filesDir, "$fileName.json").apply { - writeText(jsonParser.encodeResourceToString(resource)) - } + return File( + context.filesDir, + "$KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER/${resource.resourceType}/$fileName.json", + ) + .apply { + this.parentFile?.mkdirs() + writeText(jsonParser.encodeResourceToString(resource)) + } } /** @@ -821,6 +826,7 @@ constructor( const val PAGINATION_NEXT = "next" const val RESOURCES_PATH = "resources/" const val SYNC_LOCATION_IDS = "_syncLocations" + const val KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER = "km" /** * The list of resources whose types can be synced down as part of the Composition configs. diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/di/CoreModule.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/di/CoreModule.kt index a3a1eb6d01..7ffedd809b 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/di/CoreModule.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/di/CoreModule.kt @@ -27,10 +27,14 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import java.io.File +import java.io.FileInputStream import javax.inject.Singleton import org.hl7.fhir.r4.context.SimpleWorkerContext import org.hl7.fhir.r4.model.Parameters +import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.utils.FHIRPathEngine +import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.util.helper.TransformSupportServices @InstallIn(SingletonComponent::class) @@ -39,10 +43,29 @@ class CoreModule { @Singleton @Provides - fun provideWorkerContextProvider(): SimpleWorkerContext = + fun provideWorkerContextProvider(@ApplicationContext context: Context): SimpleWorkerContext = SimpleWorkerContext().apply { setExpansionProfile(Parameters()) isCanRunWithoutTerminology = true + context.filesDir + .resolve(ConfigurationRegistry.KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER) + .list() + ?.forEach { resourceFolder -> + context.filesDir + .resolve("${ConfigurationRegistry.KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER}/$resourceFolder") + .list() + ?.forEach { file -> + cacheResource( + FhirContext.forR4Cached() + .newJsonParser() + .parseResource( + FileInputStream( + File(context.filesDir.resolve("km/$resourceFolder/$file").toString()), + ), + ) as Resource, + ) + } + } } @Singleton From 7eb0fde1d53c1f89e7d56f90d07c7daa2b17a58a Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 20 Aug 2024 08:43:56 +0300 Subject: [PATCH 020/110] Refactor CQL Content tests --- .../fhircore/quest/CqlContentTest.kt | 183 +++++++++--------- 1 file changed, 94 insertions(+), 89 deletions(-) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index 7705221fad..bad1f79522 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -23,8 +23,10 @@ import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import java.io.File import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.Condition import org.hl7.fhir.r4.model.Library import org.hl7.fhir.r4.model.Parameters import org.hl7.fhir.r4.model.Resource @@ -40,9 +42,9 @@ import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.valueToString import org.smartregister.fhircore.quest.robolectric.RobolectricTest import org.smartregister.fhircore.quest.sdk.CqlBuilder -import org.smartregister.fhircore.quest.sdk.runBlockingOnWorkerThread @HiltAndroidTest +@OptIn(ExperimentalCoroutinesApi::class) class CqlContentTest : RobolectricTest() { @get:Rule var hiltRule = HiltAndroidRule(this) @@ -61,112 +63,115 @@ class CqlContentTest : RobolectricTest() { } @Test - fun runCqlLibraryTestForPqMedication() = runBlockingOnWorkerThread { - val resourceDir = "cql/pq-medication" - val cql = "$resourceDir/cql.txt".readFile() - - val cqlLibrary = buildCqlLibrary(cql) - - val dataBundle = - loadTestResultsSampleData().apply { - // output of test results cql is also added to input of this cql - "cql/test-results/sample" - .readDir() - .map { it.parseSampleResource() as Resource } - .forEach { addEntry().apply { resource = it } } - } + fun runCqlLibraryTestForPqMedication() = + runTest(context = UnconfinedTestDispatcher()) { + val resourceDir = "cql/pq-medication" + val cql = "$resourceDir/cql.txt".readFile() + + val cqlLibrary = buildCqlLibrary(cql) + + val dataBundle = + loadTestResultsSampleData().apply { + // output of test results cql is also added to input of this cql + "cql/test-results/sample" + .readDir() + .map { it.parseSampleResource() as Resource } + .forEach { addEntry().apply { resource = it } } + } - createTestData(dataBundle, cqlLibrary) + createTestData(dataBundle, cqlLibrary) - val result = - fhirOperator.evaluateLibrary( - cqlLibrary.url, - dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, - null, - ) as Parameters + val result = + fhirOperator.evaluateLibrary( + cqlLibrary.url, + dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, + null, + ) as Parameters - printResult(result) + printResult(result) - assertOutput( - "$resourceDir/output_medication_request.json", - result, - ResourceType.MedicationRequest, - ) - } + assertOutput( + "$resourceDir/output_medication_request.json", + result, + ResourceType.MedicationRequest, + ) + } @Test - fun runCqlLibraryTestForTestResults() = runBlockingOnWorkerThread { - val resourceDir = "cql/test-results" - val cql = "$resourceDir/cql.txt".readFile() + fun runCqlLibraryTestForTestResults() = + runTest(context = UnconfinedTestDispatcher()) { + val resourceDir = "cql/test-results" + val cql = "$resourceDir/cql.txt".readFile() - val cqlLibrary = buildCqlLibrary(cql) + val cqlLibrary = buildCqlLibrary(cql) - val dataBundle = loadTestResultsSampleData() + val dataBundle = loadTestResultsSampleData() - createTestData(dataBundle, cqlLibrary) + createTestData(dataBundle, cqlLibrary) - val result = - fhirOperator.evaluateLibrary( - cqlLibrary.url, - dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, - null, - null, - null, - ) as Parameters + val result = + fhirOperator.evaluateLibrary( + cqlLibrary.url, + dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, + null, + null, + null, + ) as Parameters - printResult(result) + printResult(result) - assertOutput("$resourceDir/sample/output_condition.json", result, ResourceType.Condition) - assertOutput( - "$resourceDir/sample/output_service_request.json", - result, - ResourceType.ServiceRequest, - ) - assertOutput( - "$resourceDir/sample/output_diagnostic_report.json", - result, - ResourceType.DiagnosticReport, - ) - } + assertOutput("$resourceDir/sample/output_condition.json", result, ResourceType.Condition) + assertOutput( + "$resourceDir/sample/output_service_request.json", + result, + ResourceType.ServiceRequest, + ) + assertOutput( + "$resourceDir/sample/output_diagnostic_report.json", + result, + ResourceType.DiagnosticReport, + ) + } @Test - fun runCqlLibraryTestForControlTest() = runBlockingOnWorkerThread { - val resourceDir = "cql/control-test" - val cql = "$resourceDir/cql.txt".readFile() - - val cqlLibrary = buildCqlLibrary(cql) - - val dataBundle = - loadTestResultsSampleData().apply { - addEntry().apply { - // questionnaire-response of test results is input of this cql - resource = - "test-results-questionnaire/questionnaire-response.json".parseSampleResourceFromFile() - as Resource + fun runCqlLibraryTestForControlTest() = + runTest(context = UnconfinedTestDispatcher()) { + val resourceDir = "cql/control-test" + val cql = "$resourceDir/cql.txt".readFile() + + val cqlLibrary = buildCqlLibrary(cql) + + val dataBundle = + loadTestResultsSampleData().apply { + addEntry().apply { + // questionnaire-response of test results is input of this cql + resource = + "test-results-questionnaire/questionnaire-response.json".parseSampleResourceFromFile() + as Resource + } } - } - createTestData(dataBundle, cqlLibrary) + createTestData(dataBundle, cqlLibrary) - val result = - fhirOperator.evaluateLibrary( - cqlLibrary.url, - dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, - null, - ) as Parameters + val result = + fhirOperator.evaluateLibrary( + cqlLibrary.url, + dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, + null, + ) as Parameters - printResult(result) + printResult(result) - Assert.assertTrue( - result.getParameterValues("OUTPUT").first().valueToString() == "Correct Result", - ) - Assert.assertEquals( - result.getParameterValues("OUTPUT").elementAt(1).valueToString(), - "\nDetails:\n" + - "Value (3.0) is in Normal G6PD Range 0-3\n" + - "Value (11.0) is in Normal Haemoglobin Range 8-12", - ) - } + Assert.assertTrue( + result.getParameterValues("OUTPUT").first().valueToString() == "Correct Result", + ) + Assert.assertEquals( + result.getParameterValues("OUTPUT").elementAt(1).valueToString(), + "\nDetails:\n" + + "Value (3.0) is in Normal G6PD Range 0-3\n" + + "Value (11.0) is in Normal Haemoglobin Range 8-12", + ) + } private fun buildCqlLibrary(cql: String): Library { val cqlCompiler = CqlBuilder.compile(cql) From 0ad5ada882ee93e44b9a6a38a112c991b236c78e Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 20 Aug 2024 08:45:11 +0300 Subject: [PATCH 021/110] =?UTF-8?q?Fix=20AppSettingModel=20unit=20tests=20?= =?UTF-8?q?=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/appsetting/AppSettingViewModel.kt | 5 ++- .../ui/appsetting/AppSettingViewModelTest.kt | 32 +++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt index 03e1cd12fb..7e1f84e86d 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt @@ -24,6 +24,7 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import java.net.UnknownHostException import javax.inject.Inject +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.RequestBody.Companion.toRequestBody @@ -101,8 +102,10 @@ constructor( } } + private val exceptionHandler = CoroutineExceptionHandler { _, exception -> Timber.e(exception) } + private fun fetchRemoteConfigurations(appId: String?, context: Context) { - viewModelScope.launch { + viewModelScope.launch(exceptionHandler) { try { showProgressBar.postValue(true) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModelTest.kt index a8889e10e3..ece7d30f2e 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModelTest.kt @@ -34,9 +34,9 @@ import io.mockk.verify import java.net.UnknownHostException import java.nio.charset.StandardCharsets import javax.inject.Inject -import kotlin.test.assertEquals import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody @@ -131,23 +131,35 @@ class AppSettingViewModelTest : RobolectricTest() { @Test fun testFetchConfigurations() = - runTest(timeout = 90.seconds) { - fhirEngine.create(Composition().apply { id = "sampleComposition" }) + runTest(timeout = 90.seconds, context = UnconfinedTestDispatcher()) { val appId = "test_app_id" appSettingViewModel.onApplicationIdChanged(appId) - coEvery { fhirResourceDataSource.getResource(any()) } returns - Bundle().apply { - addEntry().resource = - Composition().apply { - addSection().apply { this.focus = Reference().apply { reference = "Binary/123" } } - } + coEvery { + appSettingViewModel.configurationRegistry.fetchRemoteCompositionByAppId(any()) + } returns + Composition().apply { + addSection().apply { this.focus = Reference().apply { reference = "Binary/123" } } } + + coEvery { + appSettingViewModel.configurationRegistry.loadConfigurations(any(), any(), any()) + } just runs + + coEvery { appSettingViewModel.fhirResourceDataSource.post(any(), any()) } returns Bundle() + coEvery { appSettingViewModel.defaultRepository.createRemote(any(), any()) } just runs + coEvery { + appSettingViewModel.configurationRegistry.fetchRemoteImplementationGuideByAppId( + appId, + QuestBuildConfig.VERSION_CODE, + ) + } returns null + appSettingViewModel.fetchConfigurations(context) - coVerify { fhirResourceDataSource.getResource(any()) } + coVerify { appSettingViewModel.configurationRegistry.fetchRemoteCompositionByAppId(any()) } coVerify { appSettingViewModel.defaultRepository.createRemote(any(), any()) } } From 783be6de7422c478699b7fd161f2e874244cfb51 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 20 Aug 2024 11:36:29 +0300 Subject: [PATCH 022/110] Refactor Cancel previous worflow to use native commands --- .github/workflows/ci.yml | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1c16c67ae..3dfa781720 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,10 @@ on: merge_group: branches: [ main ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: FHIRCORE_USERNAME: ${{ secrets.FHIRCORE_USERNAME }} FHIRCORE_ACCESS_TOKEN: ${{ secrets.FHIRCORE_ACCESS_TOKEN }} @@ -22,12 +26,8 @@ jobs: strategy: matrix: api-level: [30] + steps: - - name: Cancel Previous workflow runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - name: Checkout 🛎️ uses: actions/checkout@v4 @@ -115,12 +115,8 @@ jobs: strategy: matrix: api-level: [30] + steps: - - name: Cancel Previous workflow runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - name: Checkout 🛎️ uses: actions/checkout@v4 @@ -207,11 +203,7 @@ jobs: strategy: matrix: api-level: [30] - steps: - - name: Cancel Previous workflow runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} + - name: Checkout 🛎️ uses: actions/checkout@v4 @@ -317,4 +309,4 @@ jobs: - name: Upload Quest module test coverage report to Codecov if: matrix.api-level == 30 # Only upload coverage on API level 30 working-directory: android - run: bash <(curl -s https://codecov.io/bash) -F quest -f "quest/build/reports/jacoco/fhircoreJacocoReport/fhircoreJacocoReport.xml" \ No newline at end of file + run: bash <(curl -s https://codecov.io/bash) -F quest -f "quest/build/reports/jacoco/fhircoreJacocoReport/fhircoreJacocoReport.xml" From 24c5b2d1f8b17b447a78f950db6a3b31bf32b8d7 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 20 Aug 2024 11:37:55 +0300 Subject: [PATCH 023/110] Upgrade CI API level to 34 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3dfa781720..e66e2ab4c2 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - api-level: [30] + api-level: [34] steps: - name: Checkout 🛎️ @@ -114,7 +114,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - api-level: [30] + api-level: [34] steps: - name: Checkout 🛎️ @@ -194,7 +194,7 @@ jobs: path: android/geowidget/build/reports - name: Upload Geowidget module test coverage report to Codecov - if: matrix.api-level == 30 # Only upload coverage on API level 30 + if: matrix.api-level == 34 # Only upload coverage on API level 34 working-directory: android run: bash <(curl -s https://codecov.io/bash) -F geowidget -f "geowidget/build/reports/jacoco/fhircoreJacocoReport/fhircoreJacocoReport.xml" @@ -202,7 +202,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - api-level: [30] + api-level: [34] - name: Checkout 🛎️ uses: actions/checkout@v4 From 5074794c9dfe192f1c2f96eae4e80943cfb1a57e Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 21 Aug 2024 16:31:01 +0300 Subject: [PATCH 024/110] Clean up Translations --- android/quest/src/main/res/values-fr/strings.xml | 4 ++-- android/quest/src/main/res/values-in/strings.xml | 6 ------ android/quest/src/main/res/values-sw/strings.xml | 1 + android/quest/src/main/res/values/strings.xml | 3 ++- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/android/quest/src/main/res/values-fr/strings.xml b/android/quest/src/main/res/values-fr/strings.xml index 4d45d4ce5e..ae4d01024d 100644 --- a/android/quest/src/main/res/values-fr/strings.xml +++ b/android/quest/src/main/res/values-fr/strings.xml @@ -99,6 +99,7 @@ Questionnaire introuvable, synchroniser tous les questionnaires pour régler ce problème Pas de visites Réponse du questionnaire invalide + Version Type de sujet manquant dans le questionnaire. Fournir Questionnaire.subjectType pour résoudre le problème. QuestionnaireConfig est requis mais manquant Erreur dans le remplissage de certains champs du questionnaire. Réponse au questionnaire non valide. @@ -126,7 +127,7 @@ Modifier Revoir les réponses Revoir - \\@android:string/cancel + Annuler Erreurs trouvées @@ -252,7 +253,6 @@ Optionnel Requis Requis\n - \u0020\u002a diff --git a/android/quest/src/main/res/values-in/strings.xml b/android/quest/src/main/res/values-in/strings.xml index 1e5339a53c..e77b34ba80 100644 --- a/android/quest/src/main/res/values-in/strings.xml +++ b/android/quest/src/main/res/values-in/strings.xml @@ -13,8 +13,6 @@ Tidak ada hasil tes ditemukan HASIL TES Tes terakhir - %1$s - 2.4 km - # Tugas Keluarga Kunjungan rutin @@ -88,11 +86,8 @@ Luaran Kehamilan Register - %1$s (%2$s) Dijadwalkan pada %1$s - GeoWidget Fragment Destination - Gagal mengekstraksi resources untuk %1$s StructureMap untuk Questionnaire tidak ada, QuestionnaireResponse disimpan Resources berhasil diekstraksi untuk %1$s @@ -101,7 +96,6 @@ Questionnaire tidak ditemukan. Sinkronkan semua kuesioner untuk memperbaiki Tidak ada kunjungan Respons kuesioner tidak valid - https://smartregister.org/app-version Versi aplikasi Jenis subjek pada kuesioner tidak ada. Berikan Questionnaire.subjectType untuk diselesaikan. QuestionnaireConfig diperlukan tetapi tidak ada. diff --git a/android/quest/src/main/res/values-sw/strings.xml b/android/quest/src/main/res/values-sw/strings.xml index d81539309a..4d8a8be8f7 100644 --- a/android/quest/src/main/res/values-sw/strings.xml +++ b/android/quest/src/main/res/values-sw/strings.xml @@ -73,4 +73,5 @@ Ramani ya Muundo Haipo kwa Hojaji , HojajiJibu limehifadhiwa Imetoa rasilimali za %1$s Imeshindwa kutoa nyenzo za dodoso %1$s + Futa yote diff --git a/android/quest/src/main/res/values/strings.xml b/android/quest/src/main/res/values/strings.xml index ac715710f0..9a21e34727 100644 --- a/android/quest/src/main/res/values/strings.xml +++ b/android/quest/src/main/res/values/strings.xml @@ -107,7 +107,7 @@ Validation on extracted resources failed. Please check the logs An error occurred generating CarePlan. Please check the logs https://smartregister.org/app-version - Application Version + Application Version Missing subject type on questionnaire. Provide Questionnaire.subjectType to resolve. QuestionnaireConfig is required but missing. Error populating some questionnaire fields. Invalid QuestionnaireResponse. @@ -132,4 +132,5 @@ Scan QR Code Place your camera over the entire QR Code to start scanning Failed to get GPS location + \\u0020\\u002a From 54b08f729b9c285f4c3973e56ae937b0672929f6 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 28 Aug 2024 11:14:41 +0300 Subject: [PATCH 025/110] Fix measure reporting --- .../report/measure/MeasureReportViewModel.kt | 18 +++++++++--------- .../measure/MeasureReportViewModelTest.kt | 8 ++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt index b44219ffd4..a5445c7ca8 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt @@ -328,16 +328,16 @@ constructor( ) { withContext(dispatcherProvider.io()) { fhirEngine.loadCqlLibraryBundle(fhirOperator, config.url) - } - measureReportRepository.evaluatePopulationMeasure( - measureUrl = config.url, - startDateFormatted = startDateFormatted, - endDateFormatted = endDateFormatted, - subjects = subjects, - existing = existingValidReports, - practitionerId = practitionerId, - ) + measureReportRepository.evaluatePopulationMeasure( + measureUrl = config.url, + startDateFormatted = startDateFormatted, + endDateFormatted = endDateFormatted, + subjects = subjects, + existing = existingValidReports, + practitionerId = practitionerId, + ) + } } else { existingValidReports } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt index 886c4d3544..37e07977c8 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt @@ -52,6 +52,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.CodeableConcept import org.hl7.fhir.r4.model.Coding @@ -109,6 +110,9 @@ class MeasureReportViewModelTest : RobolectricTest() { @Inject lateinit var resourceDataRulesExecutor: ResourceDataRulesExecutor + @OptIn(ExperimentalCoroutinesApi::class) + private val unconfinedTestDispatcher = UnconfinedTestDispatcher() + @Inject lateinit var fhirEngine: FhirEngine private val measureReportRepository: MeasureReportRepository = mockk() private val fhirOperator: FhirOperator = mockk() @@ -302,9 +306,9 @@ class MeasureReportViewModelTest : RobolectricTest() { Assert.assertNotNull(sampleSubjectViewData.family, subjectViewData?.family) } - @Test() + @Test fun testEvaluateMeasureUtilizesPreviouslyGeneratedMeasureReportIfAvailable() = - runTest(timeout = 90.seconds) { + runTest(timeout = 90.seconds, context = unconfinedTestDispatcher) { val subject = Group().apply { id = "groupId" } val testMeasureReport = MeasureReport().apply { From c01d519e2ebe201cd22b03529089d7f9cf1b43a7 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 28 Aug 2024 12:18:22 +0300 Subject: [PATCH 026/110] Fix Workflow Configuration --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e66e2ab4c2..3a259f1eb9 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -204,6 +204,7 @@ jobs: matrix: api-level: [34] + steps: - name: Checkout 🛎️ uses: actions/checkout@v4 From 8400b5fd88ad84b2127d6995b349c376af848c56 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 28 Aug 2024 19:00:56 +0300 Subject: [PATCH 027/110] =?UTF-8?q?Fix=20QuestionnaireViewModel=20unit=20t?= =?UTF-8?q?ests=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../questionnaire/QuestionnaireViewModel.kt | 92 +++++++++---------- .../QuestionnaireViewModelTest.kt | 90 ++++++++++++------ 2 files changed, 107 insertions(+), 75 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 472effbc78..989488ace8 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -31,8 +31,8 @@ import com.google.android.fhir.datacapture.validation.NotValidated import com.google.android.fhir.datacapture.validation.QuestionnaireResponseValidator import com.google.android.fhir.datacapture.validation.Valid import com.google.android.fhir.db.ResourceNotFoundException +import com.google.android.fhir.knowledge.KnowledgeManager import com.google.android.fhir.search.Search -import com.google.android.fhir.search.filter.TokenParamFilterCriterion import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.lifecycle.HiltViewModel @@ -115,6 +115,7 @@ constructor( val fhirValidatorProvider: Provider, val fhirPathDataExtractor: FhirPathDataExtractor, val configurationRegistry: ConfigurationRegistry, + val knowledgeManager: KnowledgeManager, ) : ViewModel() { private val parser = FhirContext.forR4Cached().newJsonParser() @@ -779,58 +780,53 @@ constructor( bundle.addEntry(Bundle.BundleEntryComponent().setResource(basicResource)) } - val libraryFilters = - questionnaire.cqfLibraryUrls().map { - val apply: TokenParamFilterCriterion.() -> Unit = { value = of(it.extractLogicalIdUuid()) } - apply - } - - if (libraryFilters.isNotEmpty()) { - defaultRepository.fhirEngine - .search { - filter( - Resource.RES_ID, - *libraryFilters.toTypedArray(), + questionnaire.cqfLibraryUrls().forEach { libraryUrl -> + val librariesList = + withContext(dispatcherProvider.io()) { + knowledgeManager.loadResources( + ResourceType.Library.name, + libraryUrl, ) } - .forEach { librarySearchResult -> - val result: Parameters = - fhirOperator.evaluateLibrary( - librarySearchResult.resource.url, - subject.asReference().reference, - null, - bundle, - null, - ) as Parameters - - val resources = - result.parameter.mapNotNull { cqlResultParameterComponent -> - (cqlResultParameterComponent.value ?: cqlResultParameterComponent.resource)?.let { - resultParameterResource -> - if (BuildConfig.DEBUG) { - Timber.d( - "CQL :: Param found: ${cqlResultParameterComponent.name} with value: ${ - getStringRepresentation( - resultParameterResource, - ) - }", - ) - } - - if ( - cqlResultParameterComponent.name.equals(OUTPUT_PARAMETER_KEY) && - resultParameterResource.isResource - ) { - defaultRepository.create(true, resultParameterResource as Resource) - resultParameterResource - } else { - null - } + + librariesList.forEach { baseResource -> + val result: Parameters = + fhirOperator.evaluateLibrary( + (baseResource as Library).url, + subject.asReference().reference, + null, + bundle, + null, + ) as Parameters + + val resources = + result.parameter.mapNotNull { cqlResultParameterComponent -> + (cqlResultParameterComponent.value ?: cqlResultParameterComponent.resource)?.let { + resultParameterResource -> + if (BuildConfig.DEBUG) { + Timber.d( + "CQL :: Param found: ${cqlResultParameterComponent.name} with value: ${ + getStringRepresentation( + resultParameterResource, + ) + }", + ) + } + + if ( + cqlResultParameterComponent.name.equals(OUTPUT_PARAMETER_KEY) && + resultParameterResource.isResource + ) { + defaultRepository.create(true, resultParameterResource as Resource) + resultParameterResource + } else { + null } } + } - validateWithFhirValidator(*resources.toTypedArray()) - } + validateWithFhirValidator(*resources.toTypedArray()) + } } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 2cc178efa2..05a4a966c2 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -26,6 +26,7 @@ import com.google.android.fhir.datacapture.extensions.logicalId 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.knowledge.KnowledgeManager import com.google.android.fhir.search.Search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.testing.HiltAndroidRule @@ -41,6 +42,7 @@ import io.mockk.slot import io.mockk.spyk import io.mockk.unmockkObject import io.mockk.verify +import java.io.File import java.util.Date import java.util.UUID import javax.inject.Inject @@ -49,8 +51,10 @@ import kotlin.test.assertEquals import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Address +import org.hl7.fhir.r4.model.Attachment import org.hl7.fhir.r4.model.Basic import org.hl7.fhir.r4.model.BooleanType import org.hl7.fhir.r4.model.Bundle @@ -66,6 +70,7 @@ import org.hl7.fhir.r4.model.Flag import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.IdType import org.hl7.fhir.r4.model.IntegerType +import org.hl7.fhir.r4.model.Library import org.hl7.fhir.r4.model.ListResource import org.hl7.fhir.r4.model.Location import org.hl7.fhir.r4.model.Observation @@ -103,6 +108,7 @@ import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.appendPractitionerInfo import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.decodeResourceFromString +import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.extension.find import org.smartregister.fhircore.engine.util.extension.isToday @@ -138,6 +144,8 @@ class QuestionnaireViewModelTest : RobolectricTest() { @Inject lateinit var parser: IParser + @Inject lateinit var knowledgeManager: KnowledgeManager + private lateinit var samplePatientRegisterQuestionnaire: Questionnaire private lateinit var questionnaireConfig: QuestionnaireConfig private lateinit var questionnaireViewModel: QuestionnaireViewModel @@ -207,6 +215,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirOperator = fhirOperator, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ), ) @@ -637,7 +646,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { val questionnaireViewModelInstance = QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), @@ -646,6 +655,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirValidatorProvider = fhirValidatorProvider, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ) val patientAgeLinkId = "patient-age" val newQuestionnaireConfig = @@ -1040,34 +1050,58 @@ class QuestionnaireViewModelTest : RobolectricTest() { } @Test - fun testExecuteCqlShouldInvokeRunCqlLibrary() = runTest { - val bundle = - Bundle().apply { addEntry(Bundle.BundleEntryComponent().apply { resource = patient }) } - - val questionnaire = - samplePatientRegisterQuestionnaire.copy().apply { - addExtension( - Extension().apply { - url = "https://sample.cqf-library.url" - setValue(StringType("http://smartreg.org/Library/123")) - }, - ) - } + fun testExecuteCqlShouldInvokeRunCqlLibrary() = + runTest(UnconfinedTestDispatcher()) { + val bundle = + Bundle().apply { addEntry(Bundle.BundleEntryComponent().apply { resource = patient }) } - coEvery { fhirOperator.evaluateLibrary(any(), any(), any(), any()) } returns Parameters() + val questionnaire = + samplePatientRegisterQuestionnaire.copy().apply { + addExtension( + Extension().apply { + url = "https://sample.cqf-library.url" + setValue(StringType("http://smartreg.org/Library/123")) + }, + ) + } - questionnaireViewModel.executeCql(patient, bundle, questionnaire) - fhirEngine.create(patient) + coEvery { fhirOperator.evaluateLibrary(any(), any(), any(), any()) } returns Parameters() + + val cqlLibrary = + Library().apply { + id = "Library/123" + url = "http://smartreg.org/Library/123" + name = "123" + version = "1.0.0" + status = Enumerations.PublicationStatus.ACTIVE + addContent( + Attachment().apply { + contentType = "text/cql" + data = "someCQL".toByteArray() + }, + ) + } - coVerify { - fhirOperator.evaluateLibrary( - "http://smartreg.org/Library/123", - patient.asReference().reference, - null, - expressions = setOf(), + knowledgeManager.install( + File.createTempFile(cqlLibrary.name, ".json").apply { + this.writeText(cqlLibrary.encodeResourceToString()) + }, ) + + fhirEngine.create(patient) + + questionnaireViewModel.executeCql(patient, bundle, questionnaire) + + coVerify { + fhirOperator.evaluateLibrary( + "http://smartreg.org/Library/123", + patient.asReference().reference, + null, + bundle, + null, + ) + } } - } @Test fun testGenerateCarePlan() = runTest { @@ -1259,7 +1293,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { encounterId = null, ) Assert.assertNotNull(latestQuestionnaireResponse) - Assert.assertEquals("qr1", latestQuestionnaireResponse?.id) + Assert.assertEquals("QuestionnaireResponse/qr1", latestQuestionnaireResponse?.id) } @Test @@ -1793,7 +1827,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { val questionnaireViewModelInstance = QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), @@ -1802,6 +1836,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirValidatorProvider = fhirValidatorProvider, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ) val questionnaireWithDefaultDate = Questionnaire().apply { @@ -1844,7 +1879,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { val questionnaireViewModelInstance = QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), @@ -1853,6 +1888,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirValidatorProvider = fhirValidatorProvider, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ) val questionnaireConfig1 = questionnaireConfig.copy( From b6770edd21b7129b40e216214947e50159319c6c Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Thu, 29 Aug 2024 20:10:39 +0300 Subject: [PATCH 028/110] Clean up WorkManager after running unit tests --- .../fhircore/quest/robolectric/RobolectricTest.kt | 2 +- .../fhircore/quest/robolectric/WorkManagerRule.kt | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt index 7ef8fa6368..0698d2978b 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt @@ -100,7 +100,7 @@ abstract class RobolectricTest { fun File.parseSampleResource(): IBaseResource = sanitizeSampleResourceContent(this.readText()) - fun sanitizeSampleResourceContent(content: String): IBaseResource = + private fun sanitizeSampleResourceContent(content: String): IBaseResource = content .replace("#TODAY", Date().formatDate(SDF_YYYY_MM_DD)) .replace("#NOW", DateTimeType.now().valueAsString) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/WorkManagerRule.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/WorkManagerRule.kt index 9239735acb..515eb20688 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/WorkManagerRule.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/WorkManagerRule.kt @@ -17,8 +17,10 @@ package org.smartregister.fhircore.quest.robolectric import android.util.Log +import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry import androidx.work.Configuration +import androidx.work.WorkManager import androidx.work.testing.SynchronousExecutor import androidx.work.testing.WorkManagerTestInitHelper import org.junit.rules.TestRule @@ -37,7 +39,11 @@ class WorkManagerRule : TestRule { .setExecutor(SynchronousExecutor()) .build() WorkManagerTestInitHelper.initializeTestWorkManager(context, config) - base.evaluate() + try { + base.evaluate() + } finally { + WorkManager.getInstance(ApplicationProvider.getApplicationContext()).cancelAllWork() + } } } } From 172819a6598c8bbcfcd6a42fd7c71c9755250ed4 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Thu, 29 Aug 2024 21:22:12 +0300 Subject: [PATCH 029/110] Remove skyscreamer test dependency --- android/quest/build.gradle.kts | 1 - .../java/org/smartregister/fhircore/quest/CqlContentTest.kt | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/android/quest/build.gradle.kts b/android/quest/build.gradle.kts index ca90d1471f..bf7ca9de70 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -450,7 +450,6 @@ dependencies { testImplementation(libs.navigation.testing) testImplementation(libs.kotlin.test) testImplementation(libs.work.testing) - testImplementation("org.skyscreamer:jsonassert:1.5.3") // To run only on debug builds debugImplementation(libs.ui.test.manifest) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index bad1f79522..012d5faccb 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -35,8 +35,6 @@ import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.valueToString @@ -243,6 +241,6 @@ class CqlContentTest : RobolectricTest() { println(cqlResultStr) println(expectedResource) - JSONAssert.assertEquals(expectedResource, cqlResultStr, JSONCompareMode.STRICT) + Assert.assertEquals(expectedResource, cqlResultStr) } } From e5ee043c2272d2982119b3120f18550b81a6a3b1 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Thu, 29 Aug 2024 20:14:09 +0300 Subject: [PATCH 030/110] =?UTF-8?q?Fix=20build=20=F0=9F=92=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/measure/MeasureReportViewModel.kt | 155 +++++++++--------- .../measure/MeasureReportViewModelTest.kt | 17 +- 2 files changed, 86 insertions(+), 86 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt index a5445c7ca8..fe1769d270 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt @@ -33,12 +33,12 @@ import com.google.android.material.datepicker.MaterialDatePicker import dagger.hilt.android.lifecycle.HiltViewModel import java.util.* import javax.inject.Inject +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.MeasureReport import org.hl7.fhir.r4.model.Observation @@ -170,7 +170,9 @@ constructor( ) } refreshData() - event.practitionerId?.let { evaluateMeasure(event.navController, practitionerId = it) } + event.practitionerId?.let { + viewModelScope.launch { evaluateMeasure(event.navController, practitionerId = it) } + } } is MeasureReportEvent.OnDateSelected -> { if (selectedDate != null) { @@ -262,8 +264,10 @@ constructor( return subjectData.value } + private val exceptionHandler = CoroutineExceptionHandler { _, throwable -> println(throwable) } + // TODO: Enhancement - use FhirPathEngine evaluator for data extraction - fun evaluateMeasure(navController: NavController, practitionerId: String? = null) { + suspend fun evaluateMeasure(navController: NavController, practitionerId: String? = null) { // Run evaluate measure only for existing report if (reportConfigurations.isNotEmpty()) { // Retrieve and parse dates to (2020-11-16) @@ -277,91 +281,86 @@ constructor( .parseDate(SDF_D_MMM_YYYY_WITH_COMA) ?.formatDate(SDF_YYYY_MM_DD)!! - viewModelScope.launch { - kotlin - .runCatching { - // Show Progress indicator while evaluating measure - toggleProgressIndicatorVisibility(true) - val result = - reportConfigurations.flatMap { config -> - val subjects = mutableListOf() - subjects.addAll(measureReportRepository.fetchSubjects(config)) - - // If a practitioner Id is available, add it to the list of subjects - if (practitionerId?.isNotBlank() == true && subjects.isEmpty()) { - subjects.add("${Practitioner().resourceType.name}/$practitionerId") - } + try { + // Show Progress indicator while evaluating measure + toggleProgressIndicatorVisibility(true) + val result = + reportConfigurations.flatMap { config -> + val subjects = mutableListOf() + subjects.addAll(measureReportRepository.fetchSubjects(config)) + + // If a practitioner Id is available, add it to the list of subjects + if (practitionerId?.isNotBlank() == true && subjects.isEmpty()) { + subjects.add("${Practitioner().resourceType.name}/$practitionerId") + } - val existingReports = - fhirEngine.retrievePreviouslyGeneratedMeasureReports( - startDateFormatted = startDateFormatted, - endDateFormatted = endDateFormatted, - measureUrl = config.url, - subjects = listOf(), - ) + val existingReports = + fhirEngine.retrievePreviouslyGeneratedMeasureReports( + startDateFormatted = startDateFormatted, + endDateFormatted = endDateFormatted, + measureUrl = config.url, + subjects = listOf(), + ) + + val existingValidReports = mutableListOf() - val existingValidReports = mutableListOf() - - existingReports - .groupBy { it.subject.reference } - .forEach { entry -> - if ( - entry.value.size > 1 && - entry.value.distinctBy { it.measure }.size > 1 && - entry.value.distinctBy { it.type }.size > 1 - ) { - return@forEach - } else { - existingValidReports.addAll(entry.value) - } - } - - // if report is of current month or does not exist generate a new one and replace - // existing + existingReports + .groupBy { it.subject.reference } + .forEach { entry -> if ( - endDateFormatted - .parseDate(SDF_YYYY_MM_DD)!! - .formatDate(SDF_YYYY_MMM) - .contentEquals(Date().formatDate(SDF_YYYY_MMM)) || - existingValidReports.isEmpty() || - existingValidReports.size != subjects.size + entry.value.size > 1 && + entry.value.distinctBy { it.measure }.size > 1 && + entry.value.distinctBy { it.type }.size > 1 ) { - withContext(dispatcherProvider.io()) { - fhirEngine.loadCqlLibraryBundle(fhirOperator, config.url) - - measureReportRepository.evaluatePopulationMeasure( - measureUrl = config.url, - startDateFormatted = startDateFormatted, - endDateFormatted = endDateFormatted, - subjects = subjects, - existing = existingValidReports, - practitionerId = practitionerId, - ) - } + return@forEach } else { - existingValidReports + existingValidReports.addAll(entry.value) } } - val measureReportPopulationResultList = - formatPopulationMeasureReports(result, reportConfigurations) - _measureReportPopulationResultList.addAll( - measureReportPopulationResultList, - ) - } - .onSuccess { - measureReportPopulationResults.value = _measureReportPopulationResultList - Timber.w("measureReportPopulationResults${measureReportPopulationResults.value}") - toggleProgressIndicatorVisibility(false) - // Show results of measure report for individual/population - navController.navigate(MeasureReportNavigationScreen.MeasureReportResult.route) { - launchSingleTop = true + // if report is of current month or does not exist generate a new one and replace + // existing + if ( + endDateFormatted + .parseDate(SDF_YYYY_MM_DD)!! + .formatDate(SDF_YYYY_MMM) + .contentEquals(Date().formatDate(SDF_YYYY_MMM)) || + existingValidReports.isEmpty() || + existingValidReports.size != subjects.size + ) { + fhirEngine.loadCqlLibraryBundle(fhirOperator, config.url) + + measureReportRepository.evaluatePopulationMeasure( + measureUrl = config.url, + startDateFormatted = startDateFormatted, + endDateFormatted = endDateFormatted, + subjects = subjects, + existing = existingValidReports, + practitionerId = practitionerId, + ) + } else { + existingValidReports } } - .onFailure { - Timber.w(it) - toggleProgressIndicatorVisibility(false) - } + + val measureReportPopulationResultList = + formatPopulationMeasureReports(result, reportConfigurations) + _measureReportPopulationResultList.addAll( + measureReportPopulationResultList, + ) + + // On success + measureReportPopulationResults.value = _measureReportPopulationResultList + // Timber.w("measureReportPopulationResults${measureReportPopulationResults.value}") + + // Show results of measure report for individual/population + navController.navigate(MeasureReportNavigationScreen.MeasureReportResult.route) { + launchSingleTop = true + } + } catch (e: Exception) { + Timber.e(e) + } finally { + toggleProgressIndicatorVisibility(false) } } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt index 37e07977c8..f6b790b2ca 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt @@ -92,7 +92,6 @@ import org.smartregister.fhircore.quest.data.report.measure.MeasureReportPagingS import org.smartregister.fhircore.quest.data.report.measure.MeasureReportRepository import org.smartregister.fhircore.quest.navigation.MeasureReportNavigationScreen import org.smartregister.fhircore.quest.robolectric.RobolectricTest -import org.smartregister.fhircore.quest.ui.report.measure.models.MeasureReportPopulationResult import org.smartregister.fhircore.quest.ui.shared.models.MeasureReportSubjectViewData import org.smartregister.fhircore.quest.util.mappers.MeasureReportSubjectViewDataMapper @@ -188,7 +187,7 @@ class MeasureReportViewModelTest : RobolectricTest() { url = "http://nourl.com", module = "Module1", ) - every { measureReportViewModel.evaluateMeasure(any(), any()) } just runs + coEvery { measureReportViewModel.evaluateMeasure(any(), any()) } just runs measureReportViewModel.reportTypeSelectorUiState.value = ReportTypeSelectorUiState(startDate = "21 Jan, 2022", endDate = "27 Jan, 2022") measureReportViewModel.onEvent( @@ -204,7 +203,7 @@ class MeasureReportViewModelTest : RobolectricTest() { Assert.assertEquals(viewModelConfig.first().id, reportConfiguration.id) Assert.assertEquals(viewModelConfig.first().module, reportConfiguration.module) - verify { + coVerify { measureReportViewModel.evaluateMeasure( navController = navController, practitionerId = "practitioner-id", @@ -309,7 +308,11 @@ class MeasureReportViewModelTest : RobolectricTest() { @Test fun testEvaluateMeasureUtilizesPreviouslyGeneratedMeasureReportIfAvailable() = runTest(timeout = 90.seconds, context = unconfinedTestDispatcher) { - val subject = Group().apply { id = "groupId" } + val subject = + Group().apply { + id = "groupId" + name = "Test Group" + } val testMeasureReport = MeasureReport().apply { id = "measureId" @@ -333,9 +336,6 @@ class MeasureReportViewModelTest : RobolectricTest() { module = "Module1", ) - coEvery { measureReportViewModel.formatPopulationMeasureReports(any(), any()) } returns - listOf(MeasureReportPopulationResult()) - coEvery { fhirEngine.retrievePreviouslyGeneratedMeasureReports( startDateFormatted = "2022-01-21", @@ -369,7 +369,8 @@ class MeasureReportViewModelTest : RobolectricTest() { measureReportViewModel.formatPopulationMeasureReports(listOf(testMeasureReport), any()) } - coVerify(exactly = 0) { + // TODO Confirm verification as per implementation + coVerify { measureReportRepository.evaluatePopulationMeasure(any(), any(), any(), any(), any(), any()) } } From 5746a3c929f9cad7a6c86588092b3477d7a11c19 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 2 Sep 2024 14:39:59 +0300 Subject: [PATCH 031/110] Clean up gradle dependencies configuration --- android/engine/build.gradle.kts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index f70ab00aa9..21dac68879 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -184,8 +184,8 @@ dependencies { api(libs.data.capture) { isTransitive = true exclude(group = "ca.uhn.hapi.fhir") - exclude(group = "com.google.android.fhir", module = "engine") exclude(group = "com.google.android.fhir", module = "common") + exclude(group = "org.smartregister", module = "common") exclude(group = "org.slf4j", module = "jcl-over-slf4j") } api(libs.cqf.fhir.cr) { @@ -198,23 +198,19 @@ dependencies { exclude(group = "xerces") exclude(group = "com.github.java-json-tools") exclude(group = "org.codehaus.woodstox") - exclude(group = "com.google.android.fhir", module = "common") exclude(group = "com.google.android.fhir", module = "engine") + exclude(group = "org.smartregister", module = "engine") exclude(group = "com.github.ben-manes.caffeine") } api(libs.contrib.barcode) { isTransitive = true exclude(group = "org.smartregister", module = "data-capture") exclude(group = "ca.uhn.hapi.fhir") - exclude(group = "com.google.android.fhir", module = "common") - exclude(group = "com.google.android.fhir", module = "engine") } api(libs.contrib.locationwidget) { isTransitive = true exclude(group = "org.smartregister", module = "data-capture") exclude(group = "ca.uhn.hapi.fhir") - exclude(group = "com.google.android.fhir", module = "common") - exclude(group = "com.google.android.fhir", module = "engine") } api(libs.fhir.engine) { isTransitive = true From aecff745c3d00e62f65313fa7d5a317afea29c82 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 2 Sep 2024 14:40:49 +0300 Subject: [PATCH 032/110] Move measure reporting evaluation to BG thread --- .../report/measure/MeasureReportRepository.kt | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index 110da18a5b..cbbe7b8527 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -25,6 +25,7 @@ import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject +import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.Measure @@ -35,6 +36,7 @@ import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.configuration.report.measure.ReportConfiguration import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor +import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -54,6 +56,7 @@ constructor( override val fhirPathDataExtractor: FhirPathDataExtractor, override val parser: IParser, @ApplicationContext override val context: Context, + val dispatcherProvider: DispatcherProvider, ) : DefaultRepository( fhirEngine = fhirEngine, @@ -146,17 +149,19 @@ constructor( subject: String?, practitionerId: String?, ): MeasureReport { - return fhirOperator.evaluateMeasure( - measure = - knowledgeManager - .loadResources(ResourceType.Measure.name, measureUrl, null, null, null) - .firstOrNull() as Measure, - start = startDateFormatted, - end = endDateFormatted, - reportType = reportType, - subjectId = subject, - practitioner = practitionerId.takeIf { it?.isNotBlank() == true }, - ) + return withContext(dispatcherProvider.io()) { + fhirOperator.evaluateMeasure( + measure = + knowledgeManager + .loadResources(ResourceType.Measure.name, measureUrl, null, null, null) + .firstOrNull() as Measure, + start = startDateFormatted, + end = endDateFormatted, + reportType = reportType, + subjectId = subject, + practitioner = practitionerId.takeIf { it?.isNotBlank() == true }, + ) + } } /** From d2cdd228ee46c2b04488ab39833f2945a6b8b17b Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 2 Sep 2024 15:05:06 +0300 Subject: [PATCH 033/110] Fix MeasureReportRepositoryTest --- .../quest/data/report/measure/MeasureReportRepositoryTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt index b0c43d33f4..8d0bc2e1b0 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt @@ -135,6 +135,7 @@ class MeasureReportRepositoryTest : RobolectricTest() { fhirPathDataExtractor = mockk(), parser = parser, context = ApplicationProvider.getApplicationContext(), + dispatcherProvider = dispatcherProvider ) } From 1107d3067f219540cfaa631336d491881ebff9f9 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 2 Sep 2024 15:45:50 +0300 Subject: [PATCH 034/110] Fix MeasureReportRepositoryTest --- .../quest/data/report/measure/MeasureReportRepositoryTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt index 8d0bc2e1b0..eced4b1e16 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt @@ -135,7 +135,7 @@ class MeasureReportRepositoryTest : RobolectricTest() { fhirPathDataExtractor = mockk(), parser = parser, context = ApplicationProvider.getApplicationContext(), - dispatcherProvider = dispatcherProvider + dispatcherProvider = dispatcherProvider, ) } From b8f213d77bc86a29e59efc5e81b9c49228be9a50 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 3 Sep 2024 17:59:43 +0300 Subject: [PATCH 035/110] =?UTF-8?q?Fix=20MeasureReportViewModel=20unit=20t?= =?UTF-8?q?ests=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Roy Munge --- .../report/measure/MeasureReportViewModel.kt | 3 +- .../measure/MeasureReportViewModelTest.kt | 43 +++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt index fe1769d270..302bfcd1a3 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt @@ -289,7 +289,8 @@ constructor( val subjects = mutableListOf() subjects.addAll(measureReportRepository.fetchSubjects(config)) - // If a practitioner Id is available, add it to the list of subjects + // If a practitioner Id is available and if the subjects list is empty, add it to the + // list of subjects if (practitionerId?.isNotBlank() == true && subjects.isEmpty()) { subjects.add("${Practitioner().resourceType.name}/$practitionerId") } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt index f6b790b2ca..5949ebf6e7 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt @@ -315,7 +315,7 @@ class MeasureReportViewModelTest : RobolectricTest() { } val testMeasureReport = MeasureReport().apply { - id = "measureId" + id = "MeasureReport/measureId" measure = "http://nourl.com" type = MeasureReportType.INDIVIDUAL this.subject = subject.asReference() @@ -329,13 +329,16 @@ class MeasureReportViewModelTest : RobolectricTest() { val reportConfiguration = ReportConfiguration( - id = "measureId", + id = "ReportConfiguration/measureId", title = "Measure 1", description = "Measure report for testing", url = "http://nourl.com", module = "Module1", ) + coEvery { measureReportViewModel.formatPopulationMeasureReports(any(), any()) } returns + emptyList() + coEvery { fhirEngine.retrievePreviouslyGeneratedMeasureReports( startDateFormatted = "2022-01-21", @@ -345,19 +348,8 @@ class MeasureReportViewModelTest : RobolectricTest() { ) } returns listOf(testMeasureReport) - coEvery { - measureReportRepository.evaluatePopulationMeasure( - startDateFormatted = any(), - endDateFormatted = any(), - measureUrl = any(), - subjects = any(), - existing = any(), - practitionerId = any(), - ) - } returns listOf(testMeasureReport) - coEvery { measureReportRepository.fetchSubjects(any(ReportConfiguration::class)) } returns - listOf() + listOf(subject.asReference().toString()) measureReportViewModel.reportTypeSelectorUiState.value = ReportTypeSelectorUiState(startDate = "21 Jan, 2022", endDate = "27 Jan, 2022") @@ -365,12 +357,29 @@ class MeasureReportViewModelTest : RobolectricTest() { measureReportViewModel.evaluateMeasure(navController, null) + val measureReportListSlot = slot>() + coVerify { - measureReportViewModel.formatPopulationMeasureReports(listOf(testMeasureReport), any()) + measureReportViewModel.formatPopulationMeasureReports(capture(measureReportListSlot), any()) } - // TODO Confirm verification as per implementation - coVerify { + assertEquals(testMeasureReport.id, measureReportListSlot.captured.first().id) + assertEquals(testMeasureReport.measure, measureReportListSlot.captured.first().measure) + assertEquals(testMeasureReport.type, measureReportListSlot.captured.first().type) + assertEquals( + testMeasureReport.subject.reference, + measureReportListSlot.captured.first().subject.reference, + ) + assertEquals( + testMeasureReport.period.start.toString(), + measureReportListSlot.captured.first().period.start.toString(), + ) + assertEquals( + testMeasureReport.period.end.toString(), + measureReportListSlot.captured.first().period.end.toString(), + ) + + coVerify(exactly = 0) { measureReportRepository.evaluatePopulationMeasure(any(), any(), any(), any(), any(), any()) } } From d95173f2d47b6049ec13b8bf214e638453068815 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 9 Sep 2024 09:53:05 +0300 Subject: [PATCH 036/110] Fix Measure Reporting --- .../engine/configuration/ConfigurationRegistry.kt | 2 +- .../engine/task/FhirCarePlanGeneratorTest.kt | 2 +- android/gradle/libs.versions.toml | 12 ++++++------ .../data/report/measure/MeasureReportRepository.kt | 7 +------ .../smartregister/fhircore/quest/CqlContentTest.kt | 2 +- .../ui/questionnaire/QuestionnaireViewModelTest.kt | 2 +- 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index 5aebbe77ca..bd9c71ee98 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -602,7 +602,7 @@ constructor( */ try { if (resource is MetadataResource && resource.name != null) { - knowledgeManager.install( + knowledgeManager.index( writeToFile(resource.overwriteCanonicalURL()), ) } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt index fec08a518b..908a5aa07c 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt @@ -2415,7 +2415,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { } private suspend fun installToIgManager(resource: Resource) { - knowledgeManager.install(writeToFile(resource)) + knowledgeManager.index(writeToFile(resource)) } private suspend fun loadResource( diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index fb3ad5ab0c..adb57a750e 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -6,7 +6,7 @@ androidx-camera = "1.4.0-rc01" androidx-paging = "3.3.2" androidx-test= "1.6.1" appcompat = "1.7.0" -benchmark-junit = "1.2.4" +benchmark-junit = "1.3.0" cardview = "1.0.0" commonsJexl3 = "3.2.1" compose-material-icons = "1.6.8" @@ -20,17 +20,17 @@ coverallsGradlePlugin = "2.12.2" cqfFhirCr = "3.8.0" dagger-hilt = "2.51" datastore = "1.1.1" -desugar-jdk-libs = "2.0.4" +desugar-jdk-libs = "2.1.2" dokkaBase = "1.9.20" easy-rules-jexl = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview13.1-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview12-SNAPSHOT" -fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview11-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview14-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview13-SNAPSHOT" +fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview12-SNAPSHOT" foundation = "1.6.8" fragment-ktx = "1.8.2" glide = "4.16.0" diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index cbbe7b8527..c072361ff4 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -28,9 +28,7 @@ import javax.inject.Inject import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.model.Group -import org.hl7.fhir.r4.model.Measure import org.hl7.fhir.r4.model.MeasureReport -import org.hl7.fhir.r4.model.ResourceType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.configuration.report.measure.ReportConfiguration @@ -151,10 +149,7 @@ constructor( ): MeasureReport { return withContext(dispatcherProvider.io()) { fhirOperator.evaluateMeasure( - measure = - knowledgeManager - .loadResources(ResourceType.Measure.name, measureUrl, null, null, null) - .firstOrNull() as Measure, + measureUrl = measureUrl, start = startDateFormatted, end = endDateFormatted, reportType = reportType, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index 012d5faccb..b610c16754 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -197,7 +197,7 @@ class CqlContentTest : RobolectricTest() { private suspend fun createTestData(dataBundle: Bundle, cqlLibrary: Library) { dataBundle.entry.forEach { fhirEngine.create(it.resource) } - knowledgeManager.install( + knowledgeManager.index( File.createTempFile(cqlLibrary.name, ".json").apply { this.writeText(cqlLibrary.encodeResourceToString()) }, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 05a4a966c2..c73dd678d5 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -1082,7 +1082,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { ) } - knowledgeManager.install( + knowledgeManager.index( File.createTempFile(cqlLibrary.name, ".json").apply { this.writeText(cqlLibrary.encodeResourceToString()) }, From 22e3ce313c580ce3b49515b7ac73ee54808de738 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 10 Sep 2024 15:34:55 +0300 Subject: [PATCH 037/110] Migrate Engine and Workflow libraries - Fix Measure reporting - Optimization PRs added --- android/gradle/libs.versions.toml | 4 ++-- .../quest/data/report/measure/MeasureReportRepository.kt | 6 +++++- .../quest/ui/report/measure/worker/MeasureReportWorker.kt | 8 +++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index adb57a750e..2a586d5bc4 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -28,9 +28,9 @@ fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview14-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview13-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview14-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview12-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview13-SNAPSHOT" foundation = "1.6.8" fragment-ktx = "1.8.2" glide = "4.16.0" diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index c072361ff4..b6731f693e 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -27,7 +27,9 @@ import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException +import org.hl7.fhir.instance.model.api.IBaseResource import org.hl7.fhir.r4.model.Group +import org.hl7.fhir.r4.model.Measure import org.hl7.fhir.r4.model.MeasureReport import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.app.ConfigService @@ -148,8 +150,10 @@ constructor( practitionerId: String?, ): MeasureReport { return withContext(dispatcherProvider.io()) { + val measureUrlResources: Iterable = knowledgeManager.loadResources(measureUrl) + fhirOperator.evaluateMeasure( - measureUrl = measureUrl, + measure = measureUrlResources.first() as Measure, start = startDateFormatted, end = endDateFormatted, reportType = reportType, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt index c6f272384c..b48ee9646b 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt @@ -25,6 +25,7 @@ import androidx.work.WorkManager import androidx.work.WorkerParameters import androidx.work.workDataOf import com.google.android.fhir.FhirEngine +import com.google.android.fhir.knowledge.KnowledgeManager import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.assisted.Assisted @@ -38,6 +39,7 @@ import java.util.Calendar import java.util.Date import java.util.concurrent.TimeUnit import kotlinx.coroutines.withContext +import org.hl7.fhir.instance.model.api.IBaseResource import org.hl7.fhir.r4.model.Measure import org.hl7.fhir.r4.model.MeasureReport import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry @@ -67,6 +69,7 @@ constructor( val dispatcherProvider: DefaultDispatcherProvider, val fhirOperator: FhirOperator, val fhirEngine: FhirEngine, + private val knowledgeManager: KnowledgeManager, val workManager: WorkManager, ) : CoroutineWorker(appContext, workerParams) { @@ -127,8 +130,11 @@ constructor( val measureReport: MeasureReport? = withContext(dispatcherProvider.io()) { try { + val measureUrlResources: Iterable = + knowledgeManager.loadResources(measureUrl) + fhirOperator.evaluateMeasure( - measureUrl = measureUrl, + measure = measureUrlResources.first() as Measure, start = startDateFormatted, end = endDateFormatted, reportType = MeasureReportViewModel.POPULATION, From 96dd00e48b5e275ae9e2648bf713697c9fc89f2f Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 11 Sep 2024 15:24:51 +0300 Subject: [PATCH 038/110] Refactor register filter with REL tags Signed-off-by: Elly Kitoto --- .../register/RegisterConfiguration.kt | 2 +- .../engine/data/local/DefaultRepository.kt | 244 ++++++++++-------- .../data/local/register/RegisterRepository.kt | 55 +++- 3 files changed, 193 insertions(+), 108 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt index a22be805fb..3ff86f4b85 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt @@ -39,7 +39,7 @@ data class RegisterConfiguration( val registerCard: RegisterCardConfig = RegisterCardConfig(), val fabActions: List = emptyList(), val noResults: NoResultsConfig? = null, - val pageSize: Int = 10, + val pageSize: Int = 50, val activeResourceFilters: List = listOf( ActiveResourceFilterConfig(resourceType = ResourceType.Patient, active = true), diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 85b6059587..c1b25b46ac 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.gclient.ReferenceClientParam import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.gclient.TokenClientParam import com.google.android.fhir.FhirEngine +import com.google.android.fhir.SearchResult import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.db.ResourceNotFoundException import com.google.android.fhir.get @@ -42,10 +43,6 @@ import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.Option import com.jayway.jsonpath.PathNotFoundException import dagger.hilt.android.qualifiers.ApplicationContext -import java.util.LinkedList -import java.util.UUID -import javax.inject.Inject -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement @@ -56,7 +53,6 @@ import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import org.hl7.fhir.instance.model.api.IBaseResource -import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.Enumerations import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.IdType @@ -98,6 +94,10 @@ import org.smartregister.fhircore.engine.util.extension.updateFrom import org.smartregister.fhircore.engine.util.extension.updateLastUpdated import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import timber.log.Timber +import java.util.LinkedList +import java.util.UUID +import javax.inject.Inject +import kotlin.math.min open class DefaultRepository @Inject @@ -972,63 +972,147 @@ constructor( val baseResourceConfig = fhirResourceConfig.baseResource val relatedResourcesConfig = fhirResourceConfig.relatedResources val configComputedRuleValues = configRules.configRulesComputedValues() - val search = - Search(type = baseResourceConfig.resource).apply { - applyConfiguredSortAndFilters( - resourceConfig = baseResourceConfig, - filterActiveResources = filterActiveResources, - sortData = true, - configComputedRuleValues = configComputedRuleValues, - ) - applyFilterByRelatedEntityLocationMetaTag( - baseResourceConfig.resource, - filterByRelatedEntityLocationMetaTag, - ) - if (currentPage != null && pageSize != null) { - count = pageSize - from = currentPage * pageSize + + if (filterByRelatedEntityLocationMetaTag) { + val paginatedBaseResources = mutableListOf>() + val syncLocationIds = context.retrieveRelatedEntitySyncLocationIds() + val locationIds = + syncLocationIds + .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId }} + .flatten() + .toHashSet() + + if (currentPage != null && pageSize != null) { + val countSearch = + Search(baseResourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = baseResourceConfig, + sortData = false, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + ) + } + val totalCount = fhirEngine.count(countSearch) + val searchResults = mutableListOf>() + var pageNumber = 0 + val requiredCount = (currentPage + 1) * pageSize + var count = 0 + while (count < totalCount) { + val baseResourceSearch = + createSearch( + baseResourceConfig = baseResourceConfig, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + currentPage = pageNumber, + count = COUNT, + ) + val result = fhirEngine.search(baseResourceSearch) + searchResults.addAll(result.filter { searchResult -> + when (baseResourceConfig.resource) { + ResourceType.Location -> locationIds.contains(searchResult.resource.logicalId) + else -> searchResult.resource.meta.tag.any { + it.system == context.getString(R.string.sync_strategy_related_entity_location_system) + && locationIds.contains(it.code) + } + } + }) + count += COUNT + pageNumber++ + if (searchResults.size >= requiredCount) break } + val fromIndex = currentPage * pageSize + val toIndex = (currentPage + 1) * pageSize + paginatedBaseResources.addAll(searchResults.subList(fromIndex, min(toIndex, searchResults.size))) } - val baseFhirResources = - kotlin - .runCatching { - val searchTime = System.currentTimeMillis() - val result = fhirEngine.search(search) - Timber.w( - "It took ${(System.currentTimeMillis() - searchTime) / 1000} second(s) to search resources of type ${baseResourceConfig.resource}", - ) - result - } - .onFailure { - Timber.e( - it, - "Error retrieving resources. Empty list returned by default", - ) - } - .getOrDefault(emptyList()) - - baseFhirResources.map { searchResult -> - val retrievedRelatedResources = - retrieveRelatedResources( - resources = listOf(searchResult.resource), - relatedResourcesConfigs = relatedResourcesConfig, - relatedResourceWrapper = RelatedResourceWrapper(), - configComputedRuleValues = configComputedRuleValues, - ) - val secondaryRepositoryResourceData = - secondaryResourceConfigs.retrieveSecondaryRepositoryResourceData(filterActiveResources) - RepositoryResourceData( - resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, - resource = searchResult.resource, - relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, - relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, - secondaryRepositoryResourceData = secondaryRepositoryResourceData, + paginatedBaseResources.mapResourceToRepositoryResourceData( + relatedResourcesConfig = relatedResourcesConfig, + configComputedRuleValues = configComputedRuleValues, + secondaryResourceConfigs = secondaryResourceConfigs, + filterActiveResources = filterActiveResources, + baseResourceConfig = baseResourceConfig, + ) + } else { + val baseFhirResources: List> = + kotlin + .runCatching { + val search = + createSearch( + baseResourceConfig = baseResourceConfig, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + currentPage = currentPage, + count = pageSize, + ) + fhirEngine.search(search) + } + .onFailure { + Timber.e( + t = it, + message = "Error retrieving resources. Empty list returned by default", + ) + } + .getOrDefault(emptyList()) + baseFhirResources.mapResourceToRepositoryResourceData( + relatedResourcesConfig = relatedResourcesConfig, + configComputedRuleValues = configComputedRuleValues, + secondaryResourceConfigs = secondaryResourceConfigs, + filterActiveResources = filterActiveResources, + baseResourceConfig = baseResourceConfig, ) } } } + private suspend fun List>.mapResourceToRepositoryResourceData( + relatedResourcesConfig: List, + configComputedRuleValues: Map, + secondaryResourceConfigs: List?, + filterActiveResources: List?, + baseResourceConfig: ResourceConfig, + ) = + this.map { searchResult -> + val retrievedRelatedResources = + retrieveRelatedResources( + resources = listOf(searchResult.resource), + relatedResourcesConfigs = relatedResourcesConfig, + relatedResourceWrapper = RelatedResourceWrapper(), + configComputedRuleValues = configComputedRuleValues, + ) + val secondaryRepositoryResourceData = + secondaryResourceConfigs.retrieveSecondaryRepositoryResourceData(filterActiveResources) + RepositoryResourceData( + resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, + resource = searchResult.resource, + relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, + relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, + secondaryRepositoryResourceData = secondaryRepositoryResourceData, + ) + } + + protected fun createSearch( + baseResourceConfig: ResourceConfig, + filterActiveResources: List?, + configComputedRuleValues: Map, + currentPage: Int?, + count: Int?, + ): Search { + val search = + Search(type = baseResourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = baseResourceConfig, + filterActiveResources = filterActiveResources, + sortData = true, + configComputedRuleValues = configComputedRuleValues, + ) + if (currentPage != null && count != null) { + this.count = count + from = currentPage * count + } + } + return search + } + protected fun List?.configRulesComputedValues(): Map { if (this == null) return emptyMap() val configRules = configRulesExecutor.generateRules(this) @@ -1054,54 +1138,6 @@ constructor( return secondaryRepositoryResourceDataLinkedList } - suspend fun Search.applyFilterByRelatedEntityLocationMetaTag( - baseResourceType: ResourceType, - filterByRelatedEntityLocation: Boolean, - ) { - runBlocking { - if (filterByRelatedEntityLocation) { - val system = context.getString(R.string.sync_strategy_related_entity_location_system) - val display = context.getString(R.string.sync_strategy_related_entity_location_display) - val syncLocationIds = context.retrieveRelatedEntitySyncLocationIds() - // TODO Do we want to configure when to include subLocations and parent ids? - // TODO This will require a new config model for related entity location filter - val locationIds = - syncLocationIds - .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId } } - .flatten() - .plus(syncLocationIds) - - val filters = - if ( - baseResourceType == ResourceType.Location && locationIds.isNotEmpty() - ) { // E.g where _id=uuid1,uuid2 - locationIds.map { - val apply: TokenParamFilterCriterion.() -> Unit = { value = of(it) } - apply - } - } else { - locationIds.map { code -> // The RelatedEntityLocation is retrieved from meta tag - val apply: TokenParamFilterCriterion.() -> Unit = { - value = of(Coding(system, code, display)) - } - apply - } - } - - if (filters.isNotEmpty()) { - this@applyFilterByRelatedEntityLocationMetaTag.filter( - if (baseResourceType == ResourceType.Location) { - Location.RES_ID - } else { - TokenClientParam(TAG) - }, - *filters.toTypedArray(), - ) - } - } - } - } - suspend fun retrieveUniqueIdAssignmentResource( uniqueIdAssignmentConfig: UniqueIdAssignmentConfig?, computedValuesMap: Map, @@ -1155,6 +1191,9 @@ constructor( locations.add(currentResource) retrieveSubLocations(currentResource.logicalId).forEach(resources::addLast) } + + // Include parent Location + loadResource(locationId).let { parentLocation -> locations.addFirst(parentLocation) } return locations } @@ -1182,6 +1221,7 @@ constructor( ) companion object { + const val COUNT = 100 const val SNOMED_SYSTEM = "http://hl7.org/fhir/R4B/valueset-condition-clinical.html" const val PATIENT_CONDITION_RESOLVED_CODE = "resolved" const val PATIENT_CONDITION_RESOLVED_DISPLAY = "Resolved" diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt index d8ae465d39..98d4fcfd3e 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt @@ -19,11 +19,13 @@ package org.smartregister.fhircore.engine.data.local.register import android.content.Context import ca.uhn.fhir.parser.IParser import com.google.android.fhir.FhirEngine +import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.search.Search import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.Resource +import org.hl7.fhir.r4.model.ResourceType +import org.smartregister.fhircore.engine.R import org.smartregister.fhircore.engine.configuration.ConfigType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.app.ConfigService @@ -39,8 +41,10 @@ import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid +import org.smartregister.fhircore.engine.util.extension.retrieveRelatedEntitySyncLocationIds import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import timber.log.Timber +import javax.inject.Inject class RegisterRepository @Inject @@ -98,6 +102,51 @@ constructor( val baseResourceConfig = fhirResource.baseResource val configComputedRuleValues = registerConfiguration.configRules.configRulesComputedValues() val filterByRelatedEntityLocation = registerConfiguration.filterDataByRelatedEntityLocation + val filterActiveResources = registerConfiguration.activeResourceFilters + if (filterByRelatedEntityLocation) { + val syncLocationIds = context.retrieveRelatedEntitySyncLocationIds() + val locationIds = + syncLocationIds + .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId }} + .flatten() + .toHashSet() + val countSearch = + Search(baseResourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = baseResourceConfig, + sortData = false, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + ) + } + val totalCount = fhirEngine.count(countSearch) + var searchResultsCount = 0L + var pageNumber = 0 + var count = 0 + while (count < totalCount) { + val baseResourceSearch = + createSearch( + baseResourceConfig = baseResourceConfig, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + currentPage = pageNumber, + count = COUNT, + ) + val result = fhirEngine.search(baseResourceSearch) + searchResultsCount += (result.filter { searchResult -> + when (baseResourceConfig.resource) { + ResourceType.Location -> locationIds.contains(searchResult.resource.logicalId) + else -> searchResult.resource.meta.tag.any { + it.system == context.getString(R.string.sync_strategy_related_entity_location_system) + && locationIds.contains(it.code) + } + } + }.size) + count += COUNT + pageNumber++ + } + return searchResultsCount + } val search = Search(baseResourceConfig.resource).apply { applyConfiguredSortAndFilters( @@ -106,10 +155,6 @@ constructor( filterActiveResources = registerConfiguration.activeResourceFilters, configComputedRuleValues = configComputedRuleValues, ) - applyFilterByRelatedEntityLocationMetaTag( - baseResourceType = baseResourceConfig.resource, - filterByRelatedEntityLocation = filterByRelatedEntityLocation, - ) } return search.count( onFailure = { From 055a1be80a4aeece75325c31515705f58b2f3fc7 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 11 Sep 2024 15:26:14 +0300 Subject: [PATCH 039/110] Update default pageSize to 15 Signed-off-by: Elly Kitoto --- .../engine/configuration/register/RegisterConfiguration.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt index 3ff86f4b85..3d89dfb56b 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt @@ -39,7 +39,7 @@ data class RegisterConfiguration( val registerCard: RegisterCardConfig = RegisterCardConfig(), val fabActions: List = emptyList(), val noResults: NoResultsConfig? = null, - val pageSize: Int = 50, + val pageSize: Int = 15, val activeResourceFilters: List = listOf( ActiveResourceFilterConfig(resourceType = ResourceType.Patient, active = true), From 960176c4437589c4ef47e17447bff5948d6607fc Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Wed, 11 Sep 2024 16:04:33 +0300 Subject: [PATCH 040/110] Fix loading locations on map Signed-off-by: Elly Kitoto --- .../engine/data/local/DefaultRepository.kt | 37 ++++++++++++------- .../util/extension/AndroidExtensions.kt | 13 ++----- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index c1b25b46ac..23ccc7ee91 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -981,8 +981,6 @@ constructor( .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId }} .flatten() .toHashSet() - - if (currentPage != null && pageSize != null) { val countSearch = Search(baseResourceConfig.resource).apply { applyConfiguredSortAndFilters( @@ -995,7 +993,6 @@ constructor( val totalCount = fhirEngine.count(countSearch) val searchResults = mutableListOf>() var pageNumber = 0 - val requiredCount = (currentPage + 1) * pageSize var count = 0 while (count < totalCount) { val baseResourceSearch = @@ -1018,20 +1015,34 @@ constructor( }) count += COUNT pageNumber++ - if (searchResults.size >= requiredCount) break + if (currentPage != null && pageSize != null) { + val maxPageCount = (currentPage + 1) * pageSize + if (searchResults.size >= maxPageCount) break + } } + + if (currentPage != null && pageSize != null) { val fromIndex = currentPage * pageSize val toIndex = (currentPage + 1) * pageSize - paginatedBaseResources.addAll(searchResults.subList(fromIndex, min(toIndex, searchResults.size))) + with(paginatedBaseResources) { + addAll(searchResults.subList(fromIndex, min(toIndex, searchResults.size))) + mapResourceToRepositoryResourceData( + relatedResourcesConfig = relatedResourcesConfig, + configComputedRuleValues = configComputedRuleValues, + secondaryResourceConfigs = secondaryResourceConfigs, + filterActiveResources = filterActiveResources, + baseResourceConfig = baseResourceConfig, + ) + } + } else { + searchResults.mapResourceToRepositoryResourceData( + relatedResourcesConfig = relatedResourcesConfig, + configComputedRuleValues = configComputedRuleValues, + secondaryResourceConfigs = secondaryResourceConfigs, + filterActiveResources = filterActiveResources, + baseResourceConfig = baseResourceConfig, + ) } - - paginatedBaseResources.mapResourceToRepositoryResourceData( - relatedResourcesConfig = relatedResourcesConfig, - configComputedRuleValues = configComputedRuleValues, - secondaryResourceConfigs = secondaryResourceConfigs, - filterActiveResources = filterActiveResources, - baseResourceConfig = baseResourceConfig, - ) } else { val baseFhirResources: List> = kotlin diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/AndroidExtensions.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/AndroidExtensions.kt index 211c1a746c..806075ad5b 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/AndroidExtensions.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/AndroidExtensions.kt @@ -31,14 +31,11 @@ import android.os.LocaleList import android.os.Parcelable import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import androidx.compose.ui.graphics.Color as ComposeColor import androidx.compose.ui.state.ToggleableState import androidx.core.os.bundleOf import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding -import java.io.Serializable -import java.util.Locale import kotlinx.coroutines.flow.firstOrNull import org.smartregister.fhircore.engine.datastore.syncLocationIdsProtoStore import org.smartregister.fhircore.engine.ui.theme.DangerColor @@ -49,6 +46,9 @@ import org.smartregister.fhircore.engine.ui.theme.SuccessColor import org.smartregister.fhircore.engine.ui.theme.WarningColor import org.smartregister.fhircore.engine.util.annotation.ExcludeFromJacocoGeneratedReport import timber.log.Timber +import java.io.Serializable +import java.util.Locale +import androidx.compose.ui.graphics.Color as ComposeColor const val ERROR_COLOR = "errorColor" const val PRIMARY_COLOR = "primaryColor" @@ -207,13 +207,6 @@ inline fun Bundle.parcelable(key: String): T? = else -> @Suppress("DEPRECATION") getParcelable(key) as? T } -@ExcludeFromJacocoGeneratedReport -inline fun Bundle.parcelableArrayList(key: String): ArrayList? = - when { - SDK_INT >= 33 -> getParcelableArrayList(key, T::class.java) - else -> @Suppress("DEPRECATION") getParcelableArrayList(key) - } - @ExcludeFromJacocoGeneratedReport inline fun Intent.serializable(key: String): T? = when { From c13157c2cf2b394dd260a2edde5e34350bfbbe06 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Thu, 12 Sep 2024 10:56:00 +0300 Subject: [PATCH 041/110] Refactor data structure used on base resource search results Signed-off-by: Elly Kitoto --- .../configuration/ConfigurationRegistry.kt | 32 ++++++------------- .../engine/data/local/DefaultRepository.kt | 6 ++-- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index 6dc039191f..1d69d69ac2 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -29,16 +29,6 @@ import com.google.android.fhir.get import com.google.android.fhir.knowledge.KnowledgeManager import com.google.android.fhir.sync.download.ResourceSearchParams import dagger.hilt.android.qualifiers.ApplicationContext -import java.io.File -import java.io.FileNotFoundException -import java.io.InputStreamReader -import java.net.UnknownHostException -import java.util.LinkedList -import java.util.Locale -import java.util.PropertyResourceBundle -import java.util.ResourceBundle -import javax.inject.Inject -import javax.inject.Singleton import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import okhttp3.RequestBody.Companion.toRequestBody @@ -61,8 +51,6 @@ import org.smartregister.fhircore.engine.configuration.app.ApplicationConfigurat import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.data.remote.fhir.resource.FhirResourceDataSource import org.smartregister.fhircore.engine.di.NetworkModule -import org.smartregister.fhircore.engine.domain.model.FhirResourceConfig -import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferenceKey import org.smartregister.fhircore.engine.util.SharedPreferencesHelper @@ -83,6 +71,16 @@ import org.smartregister.fhircore.engine.util.extension.updateLastUpdated import org.smartregister.fhircore.engine.util.helper.LocalizationHelper import retrofit2.HttpException import timber.log.Timber +import java.io.File +import java.io.FileNotFoundException +import java.io.InputStreamReader +import java.net.UnknownHostException +import java.util.LinkedList +import java.util.Locale +import java.util.PropertyResourceBundle +import java.util.ResourceBundle +import javax.inject.Inject +import javax.inject.Singleton @Singleton class ConfigurationRegistry @@ -730,16 +728,6 @@ constructor( } } - private fun FhirResourceConfig.dependentResourceTypes(target: MutableList) { - this.baseResource.dependentResourceTypes(target) - this.relatedResources.forEach { it.dependentResourceTypes(target) } - } - - private fun ResourceConfig.dependentResourceTypes(target: MutableList) { - target.add(resource) - relatedResources.forEach { it.dependentResourceTypes(target) } - } - suspend fun loadResourceSearchParams(): Pair>, ResourceSearchParams> { val syncConfig = retrieveResourceConfiguration(ConfigType.Sync) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 23ccc7ee91..ca14edc72e 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -974,7 +974,6 @@ constructor( val configComputedRuleValues = configRules.configRulesComputedValues() if (filterByRelatedEntityLocationMetaTag) { - val paginatedBaseResources = mutableListOf>() val syncLocationIds = context.retrieveRelatedEntitySyncLocationIds() val locationIds = syncLocationIds @@ -991,7 +990,7 @@ constructor( ) } val totalCount = fhirEngine.count(countSearch) - val searchResults = mutableListOf>() + val searchResults = LinkedList>() var pageNumber = 0 var count = 0 while (count < totalCount) { @@ -1024,8 +1023,7 @@ constructor( if (currentPage != null && pageSize != null) { val fromIndex = currentPage * pageSize val toIndex = (currentPage + 1) * pageSize - with(paginatedBaseResources) { - addAll(searchResults.subList(fromIndex, min(toIndex, searchResults.size))) + with(searchResults.subList(fromIndex, min(toIndex, searchResults.size))) { mapResourceToRepositoryResourceData( relatedResourcesConfig = relatedResourcesConfig, configComputedRuleValues = configComputedRuleValues, From 566b65d3f5dc76beeee4cf57f5f23b50aafbe4f4 Mon Sep 17 00:00:00 2001 From: Simon Njoroge Date: Thu, 12 Sep 2024 16:25:25 +0300 Subject: [PATCH 042/110] Process saveCredentials in the background --- .../engine/data/remote/shared/TokenAuthenticator.kt | 7 ++++++- .../fhircore/quest/ui/appsetting/AppSettingViewModel.kt | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt index ab3d7f305c..076d735455 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt @@ -40,6 +40,8 @@ import java.util.Base64 import javax.inject.Inject import javax.inject.Singleton import javax.net.ssl.SSLHandshakeException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.data.remote.auth.OAuthService @@ -213,8 +215,11 @@ constructor( addAccountExplicitly(newAccount, oAuthResponse.refreshToken, null) setAuthToken(newAccount, AUTH_TOKEN_TYPE, oAuthResponse.accessToken) } + // Save credentials - secureSharedPreference.saveCredentials(username, password) + CoroutineScope(dispatcherProvider.io()).launch { + secureSharedPreference.saveCredentials(username, password) + } } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt index 7e1f84e86d..2881571090 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.RequestBody.Companion.toRequestBody import org.apache.commons.lang3.StringUtils -import org.hl7.fhir.r4.model.Binary import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.Composition import org.hl7.fhir.r4.model.ResourceType From 6c32e62a2c2b42a4809ae93bc6281b9124fbb2cc Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Fri, 13 Sep 2024 17:30:34 +0300 Subject: [PATCH 043/110] Refactor code Signed-off-by: Elly Kitoto --- .../engine/data/local/DefaultRepository.kt | 8 +++---- .../data/local/register/RegisterRepository.kt | 21 +++++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 80cb05aab6..8237b5b925 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -695,12 +695,14 @@ constructor( val includedResources: Map>? = currentSearchResult.included ?.values + ?.asSequence() ?.flatten() ?.distinctBy { it.id } ?.groupBy { it.resourceType } val reverseIncludedResources: Map>? = currentSearchResult.revIncluded ?.values + ?.asSequence() ?.flatten() ?.distinctBy { it.id } ?.groupBy { it.resourceType } @@ -1206,8 +1208,7 @@ constructor( return locations } - private suspend fun retrieveSubLocations(locationId: String) = - withContext(dispatcherProvider.io()) { + private suspend fun retrieveSubLocations(locationId: String): LinkedList = fhirEngine .search( Search(type = ResourceType.Location).apply { @@ -1218,7 +1219,6 @@ constructor( }, ) .mapTo(LinkedList()) { it.resource } - } /** * A wrapper data class to hold search results. All related resources are flattened into one Map @@ -1230,7 +1230,7 @@ constructor( ) companion object { - const val COUNT = 100 + const val COUNT = 250 const val SNOMED_SYSTEM = "http://hl7.org/fhir/R4B/valueset-condition-clinical.html" const val PATIENT_CONDITION_RESOLVED_CODE = "resolved" const val PATIENT_CONDITION_RESOLVED_DISPLAY = "Resolved" diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt index 98d4fcfd3e..6e4496e135 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt @@ -108,6 +108,7 @@ constructor( val locationIds = syncLocationIds .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId }} + .asSequence() .flatten() .toHashSet() val countSearch = @@ -132,16 +133,18 @@ constructor( currentPage = pageNumber, count = COUNT, ) - val result = fhirEngine.search(baseResourceSearch) - searchResultsCount += (result.filter { searchResult -> - when (baseResourceConfig.resource) { - ResourceType.Location -> locationIds.contains(searchResult.resource.logicalId) - else -> searchResult.resource.meta.tag.any { - it.system == context.getString(R.string.sync_strategy_related_entity_location_system) - && locationIds.contains(it.code) - } + searchResultsCount += fhirEngine.search(baseResourceSearch) + .asSequence() + .map { it.resource } + .filter { resource -> + when (resource.resourceType) { + ResourceType.Location -> locationIds.contains(resource.logicalId) + else -> resource.meta.tag.any { + it.system == context.getString(R.string.sync_strategy_related_entity_location_system) + && locationIds.contains(it.code) + } } - }.size) + }.count().toLong() count += COUNT pageNumber++ } From 6ffbca0b8fb94c08cbb8e47848db91881a7bface Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Fri, 13 Sep 2024 18:40:50 +0300 Subject: [PATCH 044/110] Delete unnecessary code Signed-off-by: Elly Kitoto --- .../quest/ui/multiselect/MultiSelectViewModel.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt index 1249f7d950..d7ef3aa22d 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt @@ -26,8 +26,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.android.fhir.datacapture.extensions.logicalId import dagger.hilt.android.lifecycle.HiltViewModel -import java.util.LinkedList -import javax.inject.Inject import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import org.smartregister.fhircore.engine.data.local.DefaultRepository @@ -38,7 +36,8 @@ import org.smartregister.fhircore.engine.ui.multiselect.TreeBuilder import org.smartregister.fhircore.engine.ui.multiselect.TreeNode import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor -import timber.log.Timber +import java.util.LinkedList +import javax.inject.Inject @HiltViewModel class MultiSelectViewModel @@ -63,7 +62,6 @@ constructor( previouslySelectedNodes.values.forEach { selectedNodes[it.locationId] = it } } - val currentTime = System.currentTimeMillis() val repositoryResourceData = defaultRepository.searchResourcesRecursively( filterByRelatedEntityLocationMetaTag = false, @@ -133,9 +131,6 @@ constructor( isLoading.postValue(false) _rootTreeNodes = TreeBuilder.buildTrees(lookupItems, rootNodeIds) rootTreeNodes.addAll(_rootTreeNodes) - Timber.w( - "Building tree of resource type ${multiSelectViewConfig.resourceConfig.baseResource.resource} took ${(System.currentTimeMillis() - currentTime) / 1000} second(s)", - ) } } From 3eeb9aa9d3b8b31b9d230be6a58d27e656a3de9d Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Sun, 15 Sep 2024 03:19:35 +0300 Subject: [PATCH 045/110] Refactor retrieving related resources Signed-off-by: Elly Kitoto --- .../engine/data/local/DefaultRepository.kt | 361 +++++++----------- .../data/local/register/RegisterRepository.kt | 3 +- 2 files changed, 137 insertions(+), 227 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 8237b5b925..2c7c19b168 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -32,8 +32,6 @@ import com.google.android.fhir.db.ResourceNotFoundException import com.google.android.fhir.get import com.google.android.fhir.search.Order import com.google.android.fhir.search.Search -import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion -import com.google.android.fhir.search.filter.TokenParamFilterCriterion import com.google.android.fhir.search.has import com.google.android.fhir.search.include import com.google.android.fhir.search.revInclude @@ -474,79 +472,127 @@ constructor( } protected suspend fun retrieveRelatedResources( - resources: List, + resource: Resource, relatedResourcesConfigs: List?, - relatedResourceWrapper: RelatedResourceWrapper, configComputedRuleValues: Map, ): RelatedResourceWrapper { - val countResourceConfigs = relatedResourcesConfigs?.filter { it.resultAsCount } - countResourceConfigs?.forEach { resourceConfig -> - if (resourceConfig.searchParameter.isNullOrEmpty()) { - Timber.e("Search parameter require to perform count query. Current config: $resourceConfig") - } + val relatedResourceWrapper = RelatedResourceWrapper() + val relatedResourcesQueue = ArrayDeque?>>().apply { + addFirst(Pair(resource, relatedResourcesConfigs)) + } + while (!relatedResourcesQueue.isEmpty()) { + val (currentResource, currentRelatedResourceConfigs) = relatedResourcesQueue.removeFirst() + val filteredConfigs = relatedResourcesConfigs + ?.asSequence() + ?.filter { it.resultAsCount && !it.searchParameter.isNullOrEmpty() } + ?.toList() + + filteredConfigs?.forEach { resourceConfig -> + val search = Search(resourceConfig.resource).apply { + filter( + ReferenceClientParam(resourceConfig.searchParameter), + { value = currentResource.asReference().reference } + ) + applyConfiguredSortAndFilters( + resourceConfig = resourceConfig, + sortData = true, + configComputedRuleValues = configComputedRuleValues + ) + } - // Count for each related resource or aggregate total count in one query; as configured - if (resourceConfig.resultAsCount && !resourceConfig.searchParameter.isNullOrEmpty()) { + val key = resourceConfig.id ?: resourceConfig.resource.name if (resourceConfig.countResultConfig?.sumCounts == true) { - val search = - Search(resourceConfig.resource).apply { - val filters = - resources.map { - val apply: ReferenceParamFilterCriterion.() -> Unit = { - value = it.logicalId.asReference(it.resourceType).reference - } - apply - } - filter( - ReferenceClientParam(resourceConfig.searchParameter), - *filters.toTypedArray(), - ) - applyConfiguredSortAndFilters( - resourceConfig = resourceConfig, - sortData = true, - configComputedRuleValues = configComputedRuleValues, - ) - } - val key = resourceConfig.id ?: resourceConfig.resource.name search.count( onSuccess = { relatedResourceWrapper.relatedResourceCountMap[key] = + //TODO decide whether to use a single instance of the LinkedList or create new one LinkedList().apply { add( RelatedResourceCount( - count = it, - ), + count = it, + relatedResourceType = resourceConfig.resource, + parentResourceId = resource.logicalId + ) ) } }, onFailure = { Timber.e( it, - "Error retrieving total count for all related resourced identified by $key", + "Error retrieving total count for all related resources identified by $key" ) - }, + } ) - } else { - computeCountForEachRelatedResource( - resources = resources, - resourceConfig = resourceConfig, + } + } + + val searchResults = searchIncludedResources( + relatedResourcesConfigs = relatedResourcesConfigs, + resource = currentResource, + configComputedRuleValues = configComputedRuleValues + ) + + val fwdIncludedRelatedConfigsMap = + currentRelatedResourceConfigs?.revIncludeRelatedResourceConfigs(false) + ?.groupBy { it.searchParameter!! }?.mapValues { it.value.first() } + + val revIncludedRelatedConfigsMap = + currentRelatedResourceConfigs?.revIncludeRelatedResourceConfigs(true) + ?.groupBy { "${it.resource.name}_${it.searchParameter}".lowercase() } + ?.mapValues { it.value.first() } + + searchResults.forEach { searchResult -> + searchResult.included?.forEach { entry -> + updateResourceWrapperAndQueue( + key = entry.key, + defaultKey = entry.value.firstOrNull()?.resourceType?.name, + value = entry.value, + relatedResourcesConfigsMap = fwdIncludedRelatedConfigsMap, relatedResourceWrapper = relatedResourceWrapper, - configComputedRuleValues = configComputedRuleValues, + relatedResourcesQueue = relatedResourcesQueue + ) + } + searchResult.revIncluded?.forEach { entry -> + val (resourceType, searchParam) = entry.key + val key = "${resourceType.name}_$searchParam".lowercase() + updateResourceWrapperAndQueue( + key = key, + defaultKey = entry.value.firstOrNull()?.resourceType?.name, + value = entry.value, + relatedResourcesConfigsMap = revIncludedRelatedConfigsMap, + relatedResourceWrapper = relatedResourceWrapper, + relatedResourcesQueue = relatedResourcesQueue ) } } } - - searchIncludedResources( - relatedResourcesConfigs = relatedResourcesConfigs, - resources = resources, - relatedResourceWrapper = relatedResourceWrapper, - configComputedRuleValues = configComputedRuleValues, - ) - return relatedResourceWrapper } + private fun updateResourceWrapperAndQueue( + key: String, + defaultKey: String?, + value: List, + relatedResourcesConfigsMap: Map?, + relatedResourceWrapper: RelatedResourceWrapper, + relatedResourcesQueue: ArrayDeque?>>) { + val resourceConfigs = relatedResourcesConfigsMap?.get(key) + val id = resourceConfigs?.id ?:defaultKey + if (!id.isNullOrBlank()) { + relatedResourceWrapper.relatedResourceMap[id] = + relatedResourceWrapper.relatedResourceMap.getOrPut(id) { LinkedList() }.apply { + addAll(value) + } + value.forEach { resource -> + with(resourceConfigs?.relatedResources) { + if (!this.isNullOrEmpty()) { + relatedResourcesQueue.addLast(Pair(resource, this)) + } + } + } + } + } + protected suspend fun Search.count( onSuccess: (Long) -> Unit = {}, onFailure: (Throwable) -> Unit = { throwable -> @@ -562,54 +608,6 @@ constructor( .onFailure { throwable -> onFailure(throwable) } .getOrDefault(0) - private suspend fun computeCountForEachRelatedResource( - resources: List, - resourceConfig: ResourceConfig, - relatedResourceWrapper: RelatedResourceWrapper, - configComputedRuleValues: Map, - ) { - val relatedResourceCountLinkedList = LinkedList() - val key = resourceConfig.id ?: resourceConfig.resource.name - resources.forEach { baseResource -> - val search = - Search(type = resourceConfig.resource).apply { - filter( - ReferenceClientParam(resourceConfig.searchParameter), - { value = baseResource.logicalId.asReference(baseResource.resourceType).reference }, - ) - applyConfiguredSortAndFilters( - resourceConfig = resourceConfig, - sortData = false, - configComputedRuleValues = configComputedRuleValues, - ) - } - search.count( - onSuccess = { - relatedResourceCountLinkedList.add( - RelatedResourceCount( - relatedResourceType = resourceConfig.resource, - parentResourceId = baseResource.logicalId, - count = it, - ), - ) - }, - onFailure = { - Timber.e( - it, - "Error retrieving count for ${ - baseResource.logicalId.asReference( - baseResource.resourceType, - ) - } for related resource identified ID $key", - ) - }, - ) - } - - // Add each related resource count query result to map - relatedResourceWrapper.relatedResourceCountMap[key] = relatedResourceCountLinkedList - } - /** * This function searches for reverse/forward included resources as per the configuration; * [RelatedResourceWrapper] data class is then used to wrap the maps used to store Search Query @@ -617,41 +615,30 @@ constructor( */ private suspend fun searchIncludedResources( relatedResourcesConfigs: List?, - resources: List, - relatedResourceWrapper: RelatedResourceWrapper, + resource: Resource, configComputedRuleValues: Map, - ) { - val relatedResourcesConfigsMap = relatedResourcesConfigs?.groupBy { it.resource } - - if (!relatedResourcesConfigsMap.isNullOrEmpty()) { - if (resources.isEmpty()) return - - val firstResourceType = resources.first().resourceType + ): List> { val search = - Search(firstResourceType).apply { - val filters = - resources.map { - val apply: TokenParamFilterCriterion.() -> Unit = { value = of(it.logicalId) } - apply - } - filter(Resource.RES_ID, *filters.toTypedArray()) + Search(resource.resourceType).apply { + filter(Resource.RES_ID, { value = of(resource.logicalId) }) } - // Forward include related resources e.g. Members (Patient) referenced in Group resource + // Forward include related resources e.g. a member or managingEntity of a Group resource val forwardIncludeResourceConfigs = - relatedResourcesConfigs.revIncludeRelatedResourceConfigs(false) + relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(false) - // Reverse include related resources e.g. All CarePlans, Immunization for Patient resource + // Reverse include related resources e.g. all CarePlans, Immunizations for Patient resource val reverseIncludeResourceConfigs = - relatedResourcesConfigs.revIncludeRelatedResourceConfigs(true) + relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(true) search.apply { - reverseIncludeResourceConfigs.forEach { resourceConfig -> + val thisSearch = this + reverseIncludeResourceConfigs?.forEach { resourceConfig -> revInclude( resourceConfig.resource, ReferenceClientParam(resourceConfig.searchParameter), ) { - (this as Search).applyConfiguredSortAndFilters( + thisSearch.applyConfiguredSortAndFilters( resourceConfig = resourceConfig, sortData = true, configComputedRuleValues = configComputedRuleValues, @@ -659,12 +646,12 @@ constructor( } } - forwardIncludeResourceConfigs.forEach { resourceConfig -> + forwardIncludeResourceConfigs?.forEach { resourceConfig -> include( resourceConfig.resource, ReferenceClientParam(resourceConfig.searchParameter), ) { - (this as Search).applyConfiguredSortAndFilters( + thisSearch.applyConfiguredSortAndFilters( resourceConfig = resourceConfig, sortData = true, configComputedRuleValues = configComputedRuleValues, @@ -672,80 +659,10 @@ constructor( } } } - - searchRelatedResources( - search = search, - relatedResourcesConfigsMap = relatedResourcesConfigsMap, - relatedResourceWrapper = relatedResourceWrapper, - configComputedRuleValues = configComputedRuleValues, - ) - } - } - - private suspend fun searchRelatedResources( - search: Search, - relatedResourcesConfigsMap: Map>, - relatedResourceWrapper: RelatedResourceWrapper, - configComputedRuleValues: Map, - ) { - kotlin + return kotlin .runCatching { fhirEngine.search(search) } - .onSuccess { searchResult -> - searchResult.forEach { currentSearchResult -> - val includedResources: Map>? = - currentSearchResult.included - ?.values - ?.asSequence() - ?.flatten() - ?.distinctBy { it.id } - ?.groupBy { it.resourceType } - val reverseIncludedResources: Map>? = - currentSearchResult.revIncluded - ?.values - ?.asSequence() - ?.flatten() - ?.distinctBy { it.id } - ?.groupBy { it.resourceType } - val theRelatedResourcesMap = - mutableMapOf>().apply { - includedResources?.let { putAll(it) } - reverseIncludedResources?.let { putAll(it) } - } - theRelatedResourcesMap.forEach { entry -> - val currentResourceConfigs = relatedResourcesConfigsMap[entry.key] - - val key = // Use configured id as key otherwise default to ResourceType - if (relatedResourcesConfigsMap.containsKey(entry.key)) { - currentResourceConfigs?.firstOrNull()?.id ?: entry.key.name - } else { - entry.key.name - } - - // All nested resources flattened to one map by adding to existing list - relatedResourceWrapper.relatedResourceMap[key] = - relatedResourceWrapper.relatedResourceMap - .getOrPut(key) { LinkedList() } - .plus(entry.value) - - currentResourceConfigs?.forEach { resourceConfig -> - if (resourceConfig.relatedResources.isNotEmpty()) { - retrieveRelatedResources( - resources = entry.value, - relatedResourcesConfigs = resourceConfig.relatedResources, - relatedResourceWrapper = relatedResourceWrapper, - configComputedRuleValues = configComputedRuleValues, - ) - } - } - } - } - } - .onFailure { - Timber.e( - it, - "Error fetching configured related resources: $relatedResourcesConfigsMap", - ) - } + .onFailure { Timber.e(it, "Error fetching related resources") } + .getOrDefault(emptyList()) } private fun List.revIncludeRelatedResourceConfigs(isRevInclude: Boolean) = @@ -781,7 +698,6 @@ constructor( } } - Timber.i("Computed values map = ${computedValuesMap.values}") val search = Search(resourceConfig.resource).apply { applyConfiguredSortAndFilters( @@ -802,27 +718,26 @@ constructor( closeResource(resource = it, eventWorkflow = eventWorkflow) } - val retrievedRelatedResources = - retrieveRelatedResources( - resources = resources, + resources.forEach { resource -> + val retrievedRelatedResources = retrieveRelatedResources( + resource = resource, relatedResourcesConfigs = resourceConfig.relatedResources, - relatedResourceWrapper = RelatedResourceWrapper(), configComputedRuleValues = computedValuesMap, ) + retrievedRelatedResources.relatedResourceMap.forEach { resourcesMap -> + val filteredRelatedResources = + filterResourcesByFhirPathExpression( + resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, + resources = resourcesMap.value, + ) - retrievedRelatedResources.relatedResourceMap.forEach { resourcesMap -> - val filteredRelatedResources = - filterResourcesByFhirPathExpression( - resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, - resources = resourcesMap.value, - ) - - filteredRelatedResources.forEach { resource -> - Timber.i( - "Closing related Resource type ${resource.resourceType.name} and id ${resource.id}", - ) - if (filterRelatedResource(resource, resourceConfig)) { - closeResource(resource = resource, eventWorkflow = eventWorkflow) + filteredRelatedResources.forEach { resource -> + Timber.i( + "Closing related Resource type ${resource.resourceType.name} and id ${resource.id}", + ) + if (filterRelatedResource(resource, resourceConfig)) { + closeResource(resource = resource, eventWorkflow = eventWorkflow) + } } } } @@ -992,7 +907,7 @@ constructor( ) } val totalCount = fhirEngine.count(countSearch) - val searchResults = LinkedList>() + val searchResults = ArrayDeque>() var pageNumber = 0 var count = 0 while (count < totalCount) { @@ -1085,9 +1000,8 @@ constructor( this.map { searchResult -> val retrievedRelatedResources = retrieveRelatedResources( - resources = listOf(searchResult.resource), + resource = searchResult.resource, relatedResourcesConfigs = relatedResourcesConfig, - relatedResourceWrapper = RelatedResourceWrapper(), configComputedRuleValues = configComputedRuleValues, ) val secondaryRepositoryResourceData = @@ -1193,22 +1107,19 @@ constructor( return null } - suspend fun retrieveFlattenedSubLocations(locationId: String): LinkedList { - val locations = LinkedList() - val resources: LinkedList = retrieveSubLocations(locationId) - + suspend fun retrieveFlattenedSubLocations(locationId: String): ArrayDeque { + val locations = ArrayDeque() + val resources: ArrayDeque = retrieveSubLocations(locationId) while (resources.isNotEmpty()) { val currentResource = resources.removeFirst() locations.add(currentResource) retrieveSubLocations(currentResource.logicalId).forEach(resources::addLast) } - - // Include parent Location - loadResource(locationId).let { parentLocation -> locations.addFirst(parentLocation) } + loadResource(locationId)?.let { parentLocation -> locations.addFirst(parentLocation) } return locations } - private suspend fun retrieveSubLocations(locationId: String): LinkedList = + private suspend fun retrieveSubLocations(locationId: String): ArrayDeque = fhirEngine .search( Search(type = ResourceType.Location).apply { @@ -1218,15 +1129,15 @@ constructor( ) }, ) - .mapTo(LinkedList()) { it.resource } + .mapTo(ArrayDeque()) { it.resource } /** * A wrapper data class to hold search results. All related resources are flattened into one Map * including the nested related resources as required by the Rules Engine facts. */ data class RelatedResourceWrapper( - val relatedResourceMap: MutableMap> = mutableMapOf(), - val relatedResourceCountMap: MutableMap> = mutableMapOf(), + val relatedResourceMap: MutableMap> = mutableMapOf(), + val relatedResourceCountMap: MutableMap> = mutableMapOf(), ) companion object { diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt index 6e4496e135..e50a868683 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt @@ -193,9 +193,8 @@ constructor( val retrievedRelatedResources = retrieveRelatedResources( - resources = listOf(baseResource), + resource = baseResource, relatedResourcesConfigs = resourceConfig.relatedResources, - relatedResourceWrapper = RelatedResourceWrapper(), configComputedRuleValues = configComputedRuleValues, ) From 94c13ff1c37458597213d8034d4e299af5186c93 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Sun, 15 Sep 2024 19:32:19 +0300 Subject: [PATCH 046/110] Optimize data structures and perform parallel processing Signed-off-by: Elly Kitoto --- .../configuration/ConfigurationRegistry.kt | 3 +-- .../configuration/view/ViewProperties.kt | 15 ++++++----- .../engine/data/local/DefaultRepository.kt | 25 +++++++++---------- .../engine/ui/multiselect/MultiSelectView.kt | 9 +++---- .../util/extension/ResourceExtension.kt | 16 ++++++------ .../geowidget/screens/GeoWidgetViewModel.kt | 5 ++-- .../ui/multiselect/MultiSelectViewModel.kt | 9 +++---- .../questionnaire/QuestionnaireViewModel.kt | 13 +++++----- 8 files changed, 43 insertions(+), 52 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index 1d69d69ac2..a7106e4bf0 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -75,7 +75,6 @@ import java.io.File import java.io.FileNotFoundException import java.io.InputStreamReader import java.net.UnknownHostException -import java.util.LinkedList import java.util.Locale import java.util.PropertyResourceBundle import java.util.ResourceBundle @@ -374,7 +373,7 @@ constructor( * @return A list of strings of config files. */ private fun retrieveAssetConfigs(context: Context, appId: String): MutableList { - val filesQueue = LinkedList() + val filesQueue = ArrayDeque() val configFiles = mutableListOf() context.assets.list(String.format(BASE_CONFIG_PATH, appId))?.onEach { if (!supportedFileExtensions.contains(it.fileExtension)) { diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewProperties.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewProperties.kt index 13cc346747..ba9dd121b1 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewProperties.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewProperties.kt @@ -16,7 +16,6 @@ package org.smartregister.fhircore.engine.configuration.view -import java.util.LinkedList import kotlinx.serialization.Serializable import org.smartregister.fhircore.engine.domain.model.ViewType @@ -47,18 +46,18 @@ abstract class ViewProperties : java.io.Serializable { */ fun List.retrieveListProperties(): List { val listProperties = mutableListOf() - val viewPropertiesLinkedList: LinkedList = LinkedList(this) - while (viewPropertiesLinkedList.isNotEmpty()) { - val properties = viewPropertiesLinkedList.removeFirst() + val viewPropertiesQueue: ArrayDeque = ArrayDeque(this) + while (viewPropertiesQueue.isNotEmpty()) { + val properties = viewPropertiesQueue.removeFirst() if (properties.viewType == ViewType.LIST) { listProperties.add(properties as ListProperties) } when (properties.viewType) { - ViewType.COLUMN -> viewPropertiesLinkedList.addAll((properties as ColumnProperties).children) - ViewType.ROW -> viewPropertiesLinkedList.addAll((properties as RowProperties).children) - ViewType.CARD -> viewPropertiesLinkedList.addAll((properties as CardViewProperties).content) + ViewType.COLUMN -> viewPropertiesQueue.addAll((properties as ColumnProperties).children) + ViewType.ROW -> viewPropertiesQueue.addAll((properties as RowProperties).children) + ViewType.CARD -> viewPropertiesQueue.addAll((properties as CardViewProperties).content) ViewType.LIST -> - viewPropertiesLinkedList.addAll((properties as ListProperties).registerCard.views) + viewPropertiesQueue.addAll((properties as ListProperties).registerCard.views) else -> {} } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 2c7c19b168..3f18830a56 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -91,8 +91,8 @@ import org.smartregister.fhircore.engine.util.extension.retrieveRelatedEntitySyn import org.smartregister.fhircore.engine.util.extension.updateFrom import org.smartregister.fhircore.engine.util.extension.updateLastUpdated import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor +import org.smartregister.fhircore.engine.util.pmap import timber.log.Timber -import java.util.LinkedList import java.util.UUID import javax.inject.Inject import kotlin.math.min @@ -505,8 +505,7 @@ constructor( search.count( onSuccess = { relatedResourceWrapper.relatedResourceCountMap[key] = - //TODO decide whether to use a single instance of the LinkedList or create new one - LinkedList().apply { + mutableListOf().apply { add( RelatedResourceCount( count = it, @@ -577,10 +576,10 @@ constructor( relatedResourceWrapper: RelatedResourceWrapper, relatedResourcesQueue: ArrayDeque?>>) { val resourceConfigs = relatedResourcesConfigsMap?.get(key) - val id = resourceConfigs?.id ?:defaultKey + val id = resourceConfigs?.id ?: defaultKey if (!id.isNullOrBlank()) { relatedResourceWrapper.relatedResourceMap[id] = - relatedResourceWrapper.relatedResourceMap.getOrPut(id) { LinkedList() }.apply { + relatedResourceWrapper.relatedResourceMap.getOrPut(id) { mutableListOf() }.apply { addAll(value) } value.forEach { resource -> @@ -987,7 +986,7 @@ constructor( baseResourceConfig = baseResourceConfig, ) } - } + } as List } private suspend fun List>.mapResourceToRepositoryResourceData( @@ -997,7 +996,7 @@ constructor( filterActiveResources: List?, baseResourceConfig: ResourceConfig, ) = - this.map { searchResult -> + this.pmap { searchResult -> val retrievedRelatedResources = retrieveRelatedResources( resource = searchResult.resource, @@ -1047,10 +1046,10 @@ constructor( /** This function fetches other resources that are not linked to the base/primary resource. */ protected suspend fun List?.retrieveSecondaryRepositoryResourceData( filterActiveResources: List?, - ): LinkedList { - val secondaryRepositoryResourceDataLinkedList = LinkedList() + ): List { + val secondaryRepositoryResourceDataList = mutableListOf() this?.forEach { - secondaryRepositoryResourceDataLinkedList.addAll( + secondaryRepositoryResourceDataList.addAll( searchResourcesRecursively( fhirResourceConfig = it, filterActiveResources = filterActiveResources, @@ -1060,7 +1059,7 @@ constructor( ), ) } - return secondaryRepositoryResourceDataLinkedList + return secondaryRepositoryResourceDataList } suspend fun retrieveUniqueIdAssignmentResource( @@ -1136,8 +1135,8 @@ constructor( * including the nested related resources as required by the Rules Engine facts. */ data class RelatedResourceWrapper( - val relatedResourceMap: MutableMap> = mutableMapOf(), - val relatedResourceCountMap: MutableMap> = mutableMapOf(), + val relatedResourceMap: MutableMap> = mutableMapOf(), + val relatedResourceCountMap: MutableMap> = mutableMapOf(), ) companion object { diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt index 76688be132..212ffcceaa 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt @@ -39,7 +39,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.dp -import java.util.LinkedList import org.smartregister.fhircore.engine.domain.model.SyncLocationState @Composable @@ -134,17 +133,17 @@ fun MultiSelectCheckbox( } // Select all the nested checkboxes - val linkedList = LinkedList(currentTreeNode.children) + val treeNodeArrayDeque = ArrayDeque(currentTreeNode.children) - while (linkedList.isNotEmpty()) { - val currentNode = linkedList.removeFirst() + while (treeNodeArrayDeque.isNotEmpty()) { + val currentNode = treeNodeArrayDeque.removeFirst() syncLocationStateMap[currentNode.id] = SyncLocationState( currentNode.id, currentNode.parent?.id, ToggleableState(checked.value), ) - currentNode.children.forEach { linkedList.add(it) } + currentNode.children.forEach { treeNodeArrayDeque.addLast(it) } } }, modifier = Modifier.padding(0.dp), diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt index 9c08e1e1a4..2f87eac395 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt @@ -24,13 +24,6 @@ import com.google.android.fhir.datacapture.extensions.createQuestionnaireRespons import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.get import com.google.android.fhir.search.search -import java.time.Duration -import java.time.temporal.ChronoUnit -import java.util.Date -import java.util.LinkedList -import java.util.Locale -import java.util.UUID -import kotlin.math.abs import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.model.Base import org.hl7.fhir.r4.model.BaseDateTimeType @@ -64,7 +57,6 @@ import org.hl7.fhir.r4.model.StructureMap import org.hl7.fhir.r4.model.Task import org.hl7.fhir.r4.model.Timing import org.hl7.fhir.r4.model.Type -import org.hl7.fhir.r4.model.codesystems.AdministrativeGender import org.joda.time.Instant import org.json.JSONException import org.json.JSONObject @@ -75,6 +67,12 @@ import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import timber.log.Timber +import java.time.Duration +import java.time.temporal.ChronoUnit +import java.util.Date +import java.util.Locale +import java.util.UUID +import kotlin.math.abs const val REFERENCE = "reference" const val PARTOF = "part-of" @@ -438,7 +436,7 @@ fun ImplementationGuide.retrieveImplementationGuideDefinitionResources(): */ fun Composition.retrieveCompositionSections(): List { val sections = mutableListOf() - val sectionsQueue = LinkedList() + val sectionsQueue = ArrayDeque() this.section.forEach { if (!it.section.isNullOrEmpty()) { it.section.forEach { sectionComponent -> sectionsQueue.addLast(sectionComponent) } diff --git a/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModel.kt b/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModel.kt index 8862acba5d..dc2073653f 100644 --- a/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModel.kt +++ b/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModel.kt @@ -19,17 +19,16 @@ package org.smartregister.fhircore.geowidget.screens import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import java.util.LinkedList -import javax.inject.Inject import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.geowidget.model.GeoJsonFeature import org.smartregister.fhircore.geowidget.model.ServicePointType +import javax.inject.Inject @HiltViewModel class GeoWidgetViewModel @Inject constructor(val dispatcherProvider: DispatcherProvider) : ViewModel() { - val features = MutableLiveData>(LinkedList()) + val features = MutableLiveData>(mutableListOf()) fun getServicePointKeyToType(): Map { val map: MutableMap = HashMap() diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt index d7ef3aa22d..64799c70b9 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt @@ -36,7 +36,6 @@ import org.smartregister.fhircore.engine.ui.multiselect.TreeBuilder import org.smartregister.fhircore.engine.ui.multiselect.TreeNode import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor -import java.util.LinkedList import javax.inject.Inject @HiltViewModel @@ -161,9 +160,9 @@ constructor( rootTreeNodeMap[rootTreeNode.id] = rootTreeNode return@forEach } - val childrenList = LinkedList(rootTreeNode.children) - while (childrenList.isNotEmpty()) { - val currentNode = childrenList.removeFirst() + val treeNodeArrayDeque = ArrayDeque(rootTreeNode.children) + while (treeNodeArrayDeque.isNotEmpty()) { + val currentNode = treeNodeArrayDeque.removeFirst() if (currentNode.data.contains(other = searchTerm, ignoreCase = true)) { when { rootTreeNodeMap.containsKey(rootTreeNode.id) -> return@forEach @@ -173,7 +172,7 @@ constructor( } } } - currentNode.children.forEach { childrenList.add(it) } + currentNode.children.forEach { treeNodeArrayDeque.addLast(it) } } } rootTreeNodes.addAll(rootTreeNodeMap.values) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 472effbc78..50f1520793 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -36,11 +36,6 @@ import com.google.android.fhir.search.filter.TokenParamFilterCriterion import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.lifecycle.HiltViewModel -import java.util.Date -import java.util.LinkedList -import java.util.UUID -import javax.inject.Inject -import javax.inject.Provider import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -100,6 +95,10 @@ import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import org.smartregister.fhircore.engine.util.helper.TransformSupportServices import org.smartregister.fhircore.quest.R import timber.log.Timber +import java.util.Date +import java.util.UUID +import javax.inject.Inject +import javax.inject.Provider @HiltViewModel class QuestionnaireViewModel @@ -1042,7 +1041,7 @@ constructor( ): List { return when { subjectResourceType != null && subjectResourceIdentifier != null -> - LinkedList().apply { + mutableListOf().apply { loadResource(subjectResourceType, subjectResourceIdentifier)?.let { add(it) } val actionParametersExcludingSubject = actionParameters.filterNot { @@ -1052,7 +1051,7 @@ constructor( } addAll(retrievePopulationResources(actionParametersExcludingSubject)) } - else -> LinkedList(retrievePopulationResources(actionParameters)) + else -> retrievePopulationResources(actionParameters) } } From 20980f7a2c24d387bee2f8068c6eb6c06366ac4d Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Sun, 15 Sep 2024 21:06:09 +0300 Subject: [PATCH 047/110] Use recent version of rules engine library Signed-off-by: Elly Kitoto --- android/engine/build.gradle.kts | 2 -- android/gradle/libs.versions.toml | 7 ++----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index 7393796830..eecbfb2b87 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -176,10 +176,8 @@ dependencies { api(libs.timber) api(libs.converter.gson) api(libs.json.path) - api(libs.commons.jexl3) { exclude(group = "commons-logging", module = "commons-logging") } api(libs.easy.rules.jexl) { exclude(group = "commons-logging", module = "commons-logging") - exclude(group = "org.apache.commons", module = "commons-jexl3") } api(libs.data.capture) { isTransitive = true diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 560ed26d7e..e41305a8dd 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -8,7 +8,6 @@ androidx-test = "1.6.1" appcompat = "1.7.0" benchmark-junit = "1.2.4" cardview = "1.0.0" -commonsJexl3 = "3.2.1" compose-material-icons = "1.6.8" compressor = "3.0.1" constraintlayout = "2.1.4" @@ -22,7 +21,7 @@ dagger-hilt = "2.51" datastore = "1.1.1" desugar-jdk-libs = "2.0.4" dokkaBase = "1.8.20" -easy-rules-jexl = "4.1.1-SNAPSHOT" +easyRulesCore = "1.0.7" espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" @@ -99,7 +98,6 @@ activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } benchmark-junit = { group = "androidx.benchmark", name = "benchmark-junit4", version.ref = "benchmark-junit" } cardview = { group = "androidx.cardview", name = "cardview", version.ref = "cardview" } -commons-jexl3 = { module = "org.apache.commons:commons-jexl3", version.ref = "commonsJexl3" } compose-material-icons-core = { group = "androidx.compose.material", name = "material-icons-core", version.ref = "compose-material-icons" } compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "compose-material-icons" } compressor = { group = "id.zelory", name = "compressor", version.ref = "compressor" } @@ -121,7 +119,7 @@ data-capture = { group = "org.smartregister", name = "data-capture", version.ref datastore = { group = "androidx.datastore", name = "datastore", version.ref = "datastore" } datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore"} dokka-base = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokkaBase" } -easy-rules-jexl = { group = "org.smartregister", name = "easy-rules-jexl", version.ref = "easy-rules-jexl" } +easy-rules-jexl = { module = "io.github.dvgaba:easy-rules-jexl", version.ref = "easyRulesCore" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } fhir-common-utils = { group = "org.smartregister", name = "fhir-common-utils", version.ref = "fhir-common-utils" } fhir-engine = { group = "org.smartregister", name = "engine", version.ref = "fhir-sdk-engine" } @@ -175,7 +173,6 @@ orchestrator = { group = "androidx.test", name = "orchestrator", version.ref = " p2p-lib = { group = "org.smartregister", name = "p2p-lib", version.ref = "p2p-lib" } paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "androidx-paging" } paging-runtime-ktx = { group = "androidx.paging", name = "paging-runtime-ktx", version.ref = "androidx-paging" } -play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "playServicesLocation" } play-services-tasks = { group = "com.google.android.gms", name = "play-services-tasks", version.ref = "playServicesTasks" } preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference-ktx" } prettytime = { group = "org.ocpsoft.prettytime", name = "prettytime", version.ref = "prettytime" } From b01b59c4d730ef9e42a61f09b3479ee02cbad554 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Sun, 15 Sep 2024 22:26:58 +0300 Subject: [PATCH 048/110] Refactor implementation for decoding image resources to bitmap Signed-off-by: Elly Kitoto --- .../navigation/NavigationMenuConfig.kt | 7 +- .../view/ViewPropertiesSerializer.kt | 6 +- .../quest/ui/main/AppMainViewModel.kt | 41 +++---- .../quest/ui/profile/ProfileViewModel.kt | 45 ++++---- .../quest/util/extensions/ConfigExtensions.kt | 105 +++++------------- .../util/extensions/ConfigExtensionsKtTest.kt | 74 +++--------- 6 files changed, 97 insertions(+), 181 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/navigation/NavigationMenuConfig.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/navigation/NavigationMenuConfig.kt index b2d6ae374a..9cb66a6e51 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/navigation/NavigationMenuConfig.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/navigation/NavigationMenuConfig.kt @@ -22,6 +22,10 @@ import kotlinx.serialization.Serializable import org.smartregister.fhircore.engine.domain.model.ActionConfig import org.smartregister.fhircore.engine.util.extension.interpolate + +const val ICON_TYPE_LOCAL = "local" +const val ICON_TYPE_REMOTE = "remote" + @Serializable @Parcelize data class NavigationMenuConfig( @@ -53,9 +57,6 @@ data class ImageConfig( } } -const val ICON_TYPE_LOCAL = "local" -const val ICON_TYPE_REMOTE = "remote" - enum class ImageType { JPEG, PNG, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewPropertiesSerializer.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewPropertiesSerializer.kt index 7dec5bc376..6574ed470f 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewPropertiesSerializer.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewPropertiesSerializer.kt @@ -29,12 +29,12 @@ object ViewPropertiesSerializer : JsonContentPolymorphicSerializer(ViewProperties::class) { override fun selectDeserializer( element: JsonElement, - ): DeserializationStrategy { + ): DeserializationStrategy { val jsonObject = element.jsonObject val viewType = jsonObject[VIEW_TYPE]?.jsonPrimitive?.content - require(viewType != null && ViewType.values().contains(ViewType.valueOf(viewType))) { + require(viewType != null && ViewType.entries.toTypedArray().contains(ViewType.valueOf(viewType))) { """Ensure that supported `viewType` property is included in your register view properties configuration. - Supported types: ${ViewType.values()} + Supported types: ${ViewType.entries.toTypedArray()} Parsed JSON: $jsonObject """ .trimMargin() diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt index b170a5dde4..04cae4f44c 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt @@ -34,13 +34,6 @@ import com.google.android.fhir.FhirEngine import com.google.android.fhir.sync.CurrentSyncJobStatus import com.google.android.fhir.sync.SyncJobStatus import dagger.hilt.android.lifecycle.HiltViewModel -import java.text.SimpleDateFormat -import java.time.OffsetDateTime -import java.util.Date -import java.util.Locale -import java.util.TimeZone -import javax.inject.Inject -import kotlin.time.Duration import kotlinx.coroutines.async import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch @@ -85,9 +78,16 @@ import org.smartregister.fhircore.quest.navigation.NavigationArg import org.smartregister.fhircore.quest.ui.report.measure.worker.MeasureReportMonthPeriodWorker import org.smartregister.fhircore.quest.ui.shared.models.AppDrawerUIState import org.smartregister.fhircore.quest.ui.shared.models.QuestionnaireSubmission -import org.smartregister.fhircore.quest.util.extensions.decodeBinaryResourcesToBitmap import org.smartregister.fhircore.quest.util.extensions.handleClickEvent +import org.smartregister.fhircore.quest.util.extensions.resourceReferenceToBitMap import org.smartregister.fhircore.quest.util.extensions.schedulePeriodically +import java.text.SimpleDateFormat +import java.time.OffsetDateTime +import java.util.Date +import java.util.Locale +import java.util.TimeZone +import javax.inject.Inject +import kotlin.time.Duration @HiltViewModel class AppMainViewModel @@ -134,18 +134,19 @@ constructor( } fun retrieveIconsAsBitmap() { - navigationConfiguration.clientRegisters - .asSequence() - .filter { - it.menuIconConfig != null && - it.menuIconConfig?.type == ICON_TYPE_REMOTE && - !it.menuIconConfig!!.reference.isNullOrEmpty() - } - .decodeBinaryResourcesToBitmap( - viewModelScope, - registerRepository, - configurationRegistry.decodedImageMap, - ) + viewModelScope.launch (dispatcherProvider.io()){ + navigationConfiguration.clientRegisters + .asSequence() + .filter { + it.menuIconConfig != null && + it.menuIconConfig?.type == ICON_TYPE_REMOTE && + !it.menuIconConfig?.reference.isNullOrBlank() + }.mapNotNull { it.menuIconConfig!!.reference } + .resourceReferenceToBitMap( + fhirEngine = fhirEngine, + decodedImageMap = configurationRegistry.decodedImageMap, + ) + } } fun retrieveAppMainUiState(refreshAll: Boolean = true) { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModel.kt index be2b697647..219cd3b6e5 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModel.kt @@ -25,7 +25,6 @@ import androidx.lifecycle.viewModelScope import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.db.ResourceNotFoundException import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -55,11 +54,12 @@ import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import org.smartregister.fhircore.quest.R import org.smartregister.fhircore.quest.ui.profile.bottomSheet.ProfileBottomSheetFragment import org.smartregister.fhircore.quest.ui.profile.model.EligibleManagingEntity -import org.smartregister.fhircore.quest.util.extensions.decodeBinaryResourcesToBitmap +import org.smartregister.fhircore.quest.util.extensions.decodeImageResourcesToBitmap import org.smartregister.fhircore.quest.util.extensions.handleClickEvent -import org.smartregister.fhircore.quest.util.extensions.loadRemoteImagesBitmaps +import org.smartregister.fhircore.quest.util.extensions.resourceReferenceToBitMap import org.smartregister.fhircore.quest.util.extensions.toParamDataMap import timber.log.Timber +import javax.inject.Inject @HiltViewModel class ProfileViewModel @@ -90,19 +90,22 @@ constructor( * then transformed into bitmap for use in an Image Composable (returns null if the referenced * resource doesn't exist) */ - fun decodeBinaryResourceIconsToBitmap(profileId: String) { + suspend fun decodeBinaryResourceIconsToBitmap(profileId: String) { val profileConfig = configurationRegistry.retrieveConfiguration( configId = profileId, configType = ConfigType.Profile, ) - profileConfig.overFlowMenuItems - .filter { it.icon != null && !it.icon!!.reference.isNullOrEmpty() } - .decodeBinaryResourcesToBitmap( - viewModelScope, - registerRepository, - configurationRegistry.decodedImageMap, - ) + withContext(dispatcherProvider.io()) { + profileConfig.overFlowMenuItems + .asSequence() + .filter { it.icon != null && !it.icon?.reference.isNullOrBlank() } + .mapNotNull { it.icon!!.reference } + .resourceReferenceToBitMap( + fhirEngine = registerRepository.fhirEngine, + decodedImageMap = configurationRegistry.decodedImageMap, + ) + } } suspend fun retrieveProfileUiState( @@ -141,19 +144,13 @@ constructor( computedValuesMap = resourceData.computedValuesMap.plus(paramsMap), listResourceDataStateMap = listResourceDataStateMap, ) - if ( - listResourceDataStateMap[listProperties.id] != null && - listResourceDataStateMap[listProperties.id]?.size!! > 0 - ) { - listResourceDataStateMap[listProperties.id]?.forEach { resourceData -> - loadRemoteImagesBitmaps( - profileConfiguration.views, - registerRepository, - resourceData.computedValuesMap, - configurationRegistry.decodedImageMap, - ) - } - } + } + + withContext(dispatcherProvider.io()) { + profileConfigs.views.decodeImageResourcesToBitmap( + fhirEngine = registerRepository.fhirEngine, + decodedImageMap = configurationRegistry.decodedImageMap + ) } } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt index 713500d2ac..0fc6c394bc 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt @@ -29,8 +29,7 @@ import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.navigation.NavController import androidx.navigation.NavOptions -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.google.android.fhir.FhirEngine import org.hl7.fhir.r4.model.Binary import org.smartregister.fhircore.engine.configuration.navigation.ICON_TYPE_REMOTE import org.smartregister.fhircore.engine.configuration.navigation.NavigationMenuConfig @@ -39,15 +38,14 @@ import org.smartregister.fhircore.engine.configuration.view.ColumnProperties import org.smartregister.fhircore.engine.configuration.view.ImageProperties import org.smartregister.fhircore.engine.configuration.view.ListProperties import org.smartregister.fhircore.engine.configuration.view.RowProperties +import org.smartregister.fhircore.engine.configuration.view.ServiceCardProperties import org.smartregister.fhircore.engine.configuration.view.StackViewProperties import org.smartregister.fhircore.engine.configuration.view.ViewProperties import org.smartregister.fhircore.engine.configuration.workflow.ActionTrigger import org.smartregister.fhircore.engine.configuration.workflow.ApplicationWorkflow -import org.smartregister.fhircore.engine.data.local.register.RegisterRepository import org.smartregister.fhircore.engine.domain.model.ActionConfig import org.smartregister.fhircore.engine.domain.model.ActionParameter import org.smartregister.fhircore.engine.domain.model.ActionParameterType -import org.smartregister.fhircore.engine.domain.model.OverflowMenuItemConfig import org.smartregister.fhircore.engine.domain.model.ResourceData import org.smartregister.fhircore.engine.domain.model.ViewType import org.smartregister.fhircore.engine.util.extension.decodeJson @@ -56,6 +54,7 @@ import org.smartregister.fhircore.engine.util.extension.encodeJson import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.extension.interpolate import org.smartregister.fhircore.engine.util.extension.isIn +import org.smartregister.fhircore.engine.util.extension.loadResource import org.smartregister.fhircore.engine.util.extension.showToast import org.smartregister.fhircore.quest.R import org.smartregister.fhircore.quest.navigation.MainNavigationScreen @@ -63,7 +62,7 @@ import org.smartregister.fhircore.quest.navigation.NavigationArg import org.smartregister.fhircore.quest.ui.pdf.PdfLauncherFragment import org.smartregister.fhircore.quest.ui.shared.QuestionnaireHandler import org.smartregister.p2p.utils.startP2PScreen -import timber.log.Timber +import kotlin.collections.set const val PRACTITIONER_ID = "practitionerId" @@ -258,92 +257,48 @@ fun Array?.toParamDataMap(): Map = ?.filter { it.paramType == ActionParameterType.PARAMDATA } ?.associate { it.key to it.value } ?: emptyMap() -fun List.decodeBinaryResourcesToBitmap( - coroutineScope: CoroutineScope, - registerRepository: RegisterRepository, +suspend fun Sequence.resourceReferenceToBitMap( + fhirEngine: FhirEngine, decodedImageMap: SnapshotStateMap, ) { - this.forEach { - val resourceId = it.icon!!.reference!!.extractLogicalIdUuid() - coroutineScope.launch() { - registerRepository.loadResource(resourceId)?.let { binary -> + forEach { + val resourceId = it.extractLogicalIdUuid() + fhirEngine.loadResource(resourceId)?.let { binary -> decodedImageMap[resourceId] = binary.data.decodeToBitmap() } } - } -} - -fun Sequence.decodeBinaryResourcesToBitmap( - coroutineScope: CoroutineScope, - registerRepository: RegisterRepository, - decodedImageMap: SnapshotStateMap, -) { - this.forEach { - val resourceId = it.menuIconConfig!!.reference!!.extractLogicalIdUuid() - coroutineScope.launch() { - registerRepository.loadResource(resourceId)?.let { binary -> - decodedImageMap[resourceId] = binary.data.decodeToBitmap() - } - } - } } -suspend fun loadRemoteImagesBitmaps( - views: List, - registerRepository: RegisterRepository, - computedValuesMap: Map, +suspend fun List.decodeImageResourcesToBitmap( + fhirEngine: FhirEngine, decodedImageMap: MutableMap, ) { - suspend fun ViewProperties.loadIcons() { - when (this.viewType) { + val queue = ArrayDeque(this) + while (queue.isNotEmpty()) { + val viewProperty = queue.removeFirst() + when(viewProperty.viewType) { ViewType.IMAGE -> { - val imageProps = this as ImageProperties - if ( - !imageProps.imageConfig?.reference.isNullOrEmpty() && - imageProps.imageConfig?.type == ICON_TYPE_REMOTE - ) { - try { - val resourceId = - imageProps.imageConfig - ?.reference - ?.interpolate(computedValuesMap) - ?.extractLogicalIdUuid() - - if (resourceId != null) { - registerRepository.loadResource(resourceId)?.let { binary -> - decodedImageMap[resourceId] = binary.data.decodeToBitmap() - } + val imageProperties = (viewProperty as ImageProperties) + if (imageProperties.imageConfig != null) { + val imageConfig = imageProperties.imageConfig + if (ICON_TYPE_REMOTE.equals(imageConfig?.type, ignoreCase = true) && + !imageConfig?.reference.isNullOrBlank()) { + val resourceId = imageConfig!!.reference!! + fhirEngine.loadResource(resourceId)?.let { binary: Binary -> + decodedImageMap[resourceId] = binary.data.decodeToBitmap() } - } catch (exception: Exception) { - Timber.e("Failed to decode image with error: ${exception.message}") - throw exception } } } - ViewType.ROW -> { - val container = this as RowProperties - container.children.forEach { it.loadIcons() } - } - ViewType.COLUMN -> { - val container = this as ColumnProperties - container.children.forEach { it.loadIcons() } - } - ViewType.CARD -> { - val card = this as CardViewProperties - card.content.forEach { it.loadIcons() } - } - ViewType.LIST -> { - val list = this as ListProperties - list.registerCard.views.forEach { it.loadIcons() } - } - ViewType.STACK -> { - val stack = this as StackViewProperties - stack.children.forEach { it.loadIcons() } - } + ViewType.COLUMN -> (viewProperty as ColumnProperties).children.forEach(queue::addLast) + ViewType.ROW -> (viewProperty as RowProperties).children.forEach(queue::addLast) + ViewType.SERVICE_CARD -> (viewProperty as ServiceCardProperties).details.forEach(queue::addLast) + ViewType.CARD -> (viewProperty as CardViewProperties).content.forEach(queue::addLast) + ViewType.LIST -> (viewProperty as ListProperties).registerCard.views.forEach(queue::addLast) + ViewType.STACK -> (viewProperty as StackViewProperties).children.forEach(queue::addLast) else -> { - // Handle any other view types if needed + /**Ignore other views that cannot display images**/ } } } - views.forEach { it.loadIcons() } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt index df3750e8f3..db2020c2ba 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt @@ -37,7 +37,6 @@ import io.mockk.every import io.mockk.mockk import io.mockk.slot import io.mockk.verify -import javax.inject.Inject import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Binary @@ -80,6 +79,7 @@ import org.smartregister.fhircore.quest.navigation.MainNavigationScreen import org.smartregister.fhircore.quest.navigation.NavigationArg import org.smartregister.fhircore.quest.robolectric.RobolectricTest import org.smartregister.fhircore.quest.ui.shared.QuestionnaireHandler +import javax.inject.Inject @HiltAndroidTest class ConfigExtensionsKtTest : RobolectricTest() { @@ -673,13 +673,12 @@ class ConfigExtensionsKtTest : RobolectricTest() { fun decodeBinaryResourcesToBitmapOnNavigationMenuClientRegistersDoneCorrectly(): Unit = runBlocking { defaultRepository.create(addResourceTags = true, binaryImage) - val navigationMenuConfigs = sequenceOf(navigationMenuConfig) + val navigationMenuConfigs = sequenceOf(navigationMenuConfig).mapNotNull { it.menuIconConfig?.reference } val decodedImageMap = mutableStateMapOf() runBlocking { - navigationMenuConfigs.decodeBinaryResourcesToBitmap( - this, - registerRepository, - decodedImageMap, + navigationMenuConfigs.resourceReferenceToBitMap( + fhirEngine = fhirEngine, + decodedImageMap = decodedImageMap, ) } Assert.assertTrue(decodedImageMap.isNotEmpty()) @@ -689,10 +688,11 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Test fun decodeBinaryResourcesToBitmapOnOverflowMenuConfigDoneCorrectly(): Unit = runTest { defaultRepository.create(addResourceTags = true, binaryImage) - val navigationMenuConfigs = listOf(overflowMenuItemConfig) + val navigationMenuConfigs = sequenceOf(overflowMenuItemConfig).mapNotNull { it.icon?.reference } val decodedImageMap = mutableStateMapOf() runBlocking { - navigationMenuConfigs.decodeBinaryResourcesToBitmap(this, registerRepository, decodedImageMap) + navigationMenuConfigs.resourceReferenceToBitMap( fhirEngine = fhirEngine, + decodedImageMap = decodedImageMap,) } Assert.assertTrue(decodedImageMap.isNotEmpty()) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) @@ -702,12 +702,7 @@ class ConfigExtensionsKtTest : RobolectricTest() { fun testImageBitmapUpdatedCorrectlyGivenProfileConfiguration(): Unit = runTest { defaultRepository.create(addResourceTags = true, binaryImage) val decodedImageMap = mutableStateMapOf() - loadRemoteImagesBitmaps( - profileConfiguration.views, - registerRepository = registerRepository, - computedValuesMap = emptyMap(), - configurationRegistry.decodedImageMap, - ) + profileConfiguration.views.decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) Assert.assertTrue(decodedImageMap.isNotEmpty()) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) } @@ -717,12 +712,7 @@ class ConfigExtensionsKtTest : RobolectricTest() { val cardViewProperties = profileConfiguration.views[0] as CardViewProperties defaultRepository.create(addResourceTags = true, binaryImage) val decodedImageMap = mutableStateMapOf() - loadRemoteImagesBitmaps( - listOf(cardViewProperties), - registerRepository = registerRepository, - computedValuesMap = emptyMap(), - decodedImageMap, - ) + listOf(cardViewProperties).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } @@ -732,12 +722,7 @@ class ConfigExtensionsKtTest : RobolectricTest() { val cardViewProperties = profileConfiguration.views[0] as CardViewProperties defaultRepository.create(addResourceTags = true, binaryImage) val decodedImageMap = mutableStateMapOf() - loadRemoteImagesBitmaps( - listOf(cardViewProperties.content[0]), - registerRepository = registerRepository, - computedValuesMap = emptyMap(), - decodedImageMap, - ) + listOf(cardViewProperties.content[0]).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } @@ -748,12 +733,7 @@ class ConfigExtensionsKtTest : RobolectricTest() { val listViewProperties = cardViewProperties.content[0] as ListProperties val decodedImageMap = mutableStateMapOf() defaultRepository.create(addResourceTags = true, binaryImage) - loadRemoteImagesBitmaps( - listOf(listViewProperties.registerCard.views[0]), - registerRepository = registerRepository, - computedValuesMap = emptyMap(), - decodedImageMap, - ) + listOf(listViewProperties.registerCard.views[0]).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } @@ -765,12 +745,7 @@ class ConfigExtensionsKtTest : RobolectricTest() { val columnProperties = listViewProperties.registerCard.views[0] as ColumnProperties defaultRepository.create(addResourceTags = true, binaryImage) val decodedImageMap = mutableStateMapOf() - loadRemoteImagesBitmaps( - listOf(columnProperties.children[0]), - registerRepository = registerRepository, - computedValuesMap = emptyMap(), - decodedImageMap, - ) + listOf(columnProperties.children[0]).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } @@ -793,15 +768,8 @@ class ConfigExtensionsKtTest : RobolectricTest() { ), ), ) - val emptyComputedValuesMap = mutableMapOf() val decodedImageMap = mutableStateMapOf() - - loadRemoteImagesBitmaps( - listOf(rowProperties), - registerRepository = registerRepository, - computedValuesMap = emptyComputedValuesMap, - decodedImageMap = decodedImageMap, - ) + listOf(rowProperties).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) Assert.assertTrue(decodedImageMap.isEmpty()) Assert.assertTrue(!decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) } @@ -819,28 +787,22 @@ class ConfigExtensionsKtTest : RobolectricTest() { imageConfig = ImageConfig( type = ICON_TYPE_REMOTE, - reference = "null Reference", + reference = "imageReference", ), ), ), ) - val emptyComputedValuesMap = mutableMapOf() val decodedImageMap = mutableStateMapOf() coEvery { defaultRepository.loadResource(anyString()) } returns Binary().apply { - this.id = "null Reference" + this.id = "imageReference" this.contentType = "image/jpeg" this.data = "gibberish value".toByteArray() } - loadRemoteImagesBitmaps( - listOf(rowProperties), - registerRepository = registerRepository, - computedValuesMap = emptyComputedValuesMap, - decodedImageMap = decodedImageMap, - ) + listOf(rowProperties).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) Assert.assertTrue(decodedImageMap.isEmpty()) - Assert.assertTrue(!decodedImageMap.containsKey("null Reference")) + Assert.assertTrue(!decodedImageMap.containsKey("imageReference")) } } From b232ccef38bdca67f7686e848137d3120f60a891 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Mon, 16 Sep 2024 16:24:14 +0300 Subject: [PATCH 049/110] Fix related resource count on register Signed-off-by: Elly Kitoto --- .../register/RegisterConfiguration.kt | 3 +- .../engine/data/local/DefaultRepository.kt | 13 +++-- .../ui/components/register/RegisterFooter.kt | 18 +------ android/gradle/libs.versions.toml | 4 +- .../data/register/RegisterPagingSource.kt | 16 +++--- .../quest/ui/register/RegisterFragment.kt | 6 +-- .../quest/ui/register/RegisterScreen.kt | 8 ++- .../quest/ui/register/RegisterViewModel.kt | 49 ++++++++++--------- .../register/components/RegisterCardList.kt | 28 +++++++---- .../ui/register/RegisterViewModelTest.kt | 4 +- 10 files changed, 72 insertions(+), 77 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt index 3d89dfb56b..d96d3e657a 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt @@ -39,7 +39,7 @@ data class RegisterConfiguration( val registerCard: RegisterCardConfig = RegisterCardConfig(), val fabActions: List = emptyList(), val noResults: NoResultsConfig? = null, - val pageSize: Int = 15, + val pageSize: Int = 10, val activeResourceFilters: List = listOf( ActiveResourceFilterConfig(resourceType = ResourceType.Patient, active = true), @@ -50,6 +50,7 @@ data class RegisterConfiguration( val filterDataByRelatedEntityLocation: Boolean = false, val topScreenSection: TopScreenSectionConfig? = null, val onSearchByQrSingleResultActions: List? = null, + val infiniteScroll: Boolean = false ) : Configuration() { val onSearchByQrSingleResultValidActions = onSearchByQrSingleResultActions?.filter { it.trigger == ActionTrigger.ON_SEARCH_SINGLE_RESULT } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 3f18830a56..50f46b433a 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -91,7 +91,6 @@ import org.smartregister.fhircore.engine.util.extension.retrieveRelatedEntitySyn import org.smartregister.fhircore.engine.util.extension.updateFrom import org.smartregister.fhircore.engine.util.extension.updateLastUpdated import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor -import org.smartregister.fhircore.engine.util.pmap import timber.log.Timber import java.util.UUID import javax.inject.Inject @@ -482,12 +481,12 @@ constructor( } while (!relatedResourcesQueue.isEmpty()) { val (currentResource, currentRelatedResourceConfigs) = relatedResourcesQueue.removeFirst() - val filteredConfigs = relatedResourcesConfigs + val relatedResourceCountConfigs = currentRelatedResourceConfigs ?.asSequence() ?.filter { it.resultAsCount && !it.searchParameter.isNullOrEmpty() } ?.toList() - filteredConfigs?.forEach { resourceConfig -> + relatedResourceCountConfigs?.forEach { resourceConfig -> val search = Search(resourceConfig.resource).apply { filter( ReferenceClientParam(resourceConfig.searchParameter), @@ -504,8 +503,8 @@ constructor( if (resourceConfig.countResultConfig?.sumCounts == true) { search.count( onSuccess = { - relatedResourceWrapper.relatedResourceCountMap[key] = - mutableListOf().apply { + relatedResourceWrapper.relatedResourceCountMap.getOrPut(key) { mutableListOf()} + .apply { add( RelatedResourceCount( count = it, @@ -986,7 +985,7 @@ constructor( baseResourceConfig = baseResourceConfig, ) } - } as List + } } private suspend fun List>.mapResourceToRepositoryResourceData( @@ -996,7 +995,7 @@ constructor( filterActiveResources: List?, baseResourceConfig: ResourceConfig, ) = - this.pmap { searchResult -> + this.map { searchResult -> val retrievedRelatedResources = retrieveRelatedResources( resource = searchResult.resource, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/RegisterFooter.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/RegisterFooter.kt index e6dc80a54a..93a64f7537 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/RegisterFooter.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/RegisterFooter.kt @@ -34,7 +34,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.smartregister.fhircore.engine.R -import org.smartregister.fhircore.engine.configuration.navigation.NavigationMenuConfig import org.smartregister.fhircore.engine.ui.theme.GreyTextColor import org.smartregister.fhircore.engine.util.annotation.PreviewWithBackgroundExcludeGenerated @@ -43,8 +42,7 @@ const val SEARCH_FOOTER_TAG = "searchFooterTag" const val SEARCH_FOOTER_PREVIOUS_BUTTON_TAG = "searchFooterPreviousButtonTag" const val SEARCH_FOOTER_NEXT_BUTTON_TAG = "searchFooterNextButtonTag" const val SEARCH_FOOTER_PAGINATION_TAG = "searchFooterPaginationTag" -const val PADDING_BOTTOM_WITH_FAB = 80 -const val PADDING_BOTTOM_WITHOUT_FAB = 32 + @Composable fun RegisterFooter( @@ -54,21 +52,9 @@ fun RegisterFooter( previousButtonClickListener: () -> Unit, nextButtonClickListener: () -> Unit, modifier: Modifier = Modifier, - fabActions: List? = null, ) { if (resultCount > 0) { - Row( - modifier = - modifier - .fillMaxWidth() - .testTag(SEARCH_FOOTER_TAG) - .padding( - bottom = - if (!fabActions.isNullOrEmpty() && fabActions.first().visible) { - PADDING_BOTTOM_WITH_FAB.dp - } else PADDING_BOTTOM_WITHOUT_FAB.dp, - ), - ) { + Row(modifier = modifier.fillMaxWidth().testTag(SEARCH_FOOTER_TAG)) { Box( modifier = modifier.weight(1f).padding(4.dp).wrapContentWidth(Alignment.Start), ) { diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index e41305a8dd..97af931a6e 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -21,7 +21,7 @@ dagger-hilt = "2.51" datastore = "1.1.1" desugar-jdk-libs = "2.0.4" dokkaBase = "1.8.20" -easyRulesCore = "1.0.7" +easyRulesCore = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" @@ -119,7 +119,7 @@ data-capture = { group = "org.smartregister", name = "data-capture", version.ref datastore = { group = "androidx.datastore", name = "datastore", version.ref = "datastore" } datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore"} dokka-base = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokkaBase" } -easy-rules-jexl = { module = "io.github.dvgaba:easy-rules-jexl", version.ref = "easyRulesCore" } +easy-rules-jexl = { group = "org.smartregister", name = "easy-rules-jexl", version.ref = "easyRulesCore" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } fhir-common-utils = { group = "org.smartregister", name = "fhir-common-utils", version.ref = "fhir-common-utils" } fhir-engine = { group = "org.smartregister", name = "engine", version.ref = "fhir-sdk-engine" } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/register/RegisterPagingSource.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/register/RegisterPagingSource.kt index ade0d08f77..42e3ef14b7 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/register/RegisterPagingSource.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/register/RegisterPagingSource.kt @@ -64,16 +64,10 @@ class RegisterPagingSource( ) val prevKey = - when { - _registerPagingSourceState.loadAll -> if (currentPage == 0) null else currentPage - 1 - else -> null - } + if (_registerPagingSourceState.loadAll && currentPage > 0) currentPage - 1 else null val nextKey = - when { - _registerPagingSourceState.loadAll -> - if (registerData.isNotEmpty()) currentPage + 1 else null - else -> null - } + if (_registerPagingSourceState.loadAll && registerData.isNotEmpty()) currentPage + 1 + else null val data = registerData.map { repositoryResourceData -> @@ -87,9 +81,13 @@ class RegisterPagingSource( } catch (exception: SQLException) { Timber.e(exception) LoadResult.Error(exception) + } catch (exception: Exception) { + Timber.e(exception) + LoadResult.Error(exception) } } + @Synchronized fun setPatientPagingSourceState(registerPagingSourceState: RegisterPagingSourceState) { this._registerPagingSourceState = registerPagingSourceState } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt index 3d69aae807..eb9b04d65e 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt @@ -46,7 +46,6 @@ import com.google.android.fhir.sync.CurrentSyncJobStatus import com.google.android.fhir.sync.SyncJobStatus import com.google.android.fhir.sync.SyncOperation import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -69,6 +68,7 @@ import org.smartregister.fhircore.quest.ui.shared.viewmodels.SearchViewModel import org.smartregister.fhircore.quest.util.extensions.handleClickEvent import org.smartregister.fhircore.quest.util.extensions.hookSnackBar import org.smartregister.fhircore.quest.util.extensions.rememberLifecycleEvent +import javax.inject.Inject @ExperimentalMaterialApi @AndroidEntryPoint @@ -128,11 +128,11 @@ class RegisterFragment : Fragment(), OnSyncListener { AppTheme { val pagingItems = - registerViewModel.paginatedRegisterData + registerViewModel.registerData .collectAsState(emptyFlow()) .value .collectAsLazyPagingItems() - // Register screen provides access to the side navigation + Scaffold( drawerGesturesEnabled = scaffoldState.drawerState.isOpen, scaffoldState = scaffoldState, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterScreen.kt index 504066fba7..5facb82ae5 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterScreen.kt @@ -181,10 +181,7 @@ fun RegisterScreen( Box( modifier = Modifier.fillMaxWidth().background(Color.White).weight(1f), ) { - if ( - registerUiState.totalRecordsCount > 0 && - registerUiState.registerConfiguration?.registerCard != null - ) { + if (registerUiState.registerConfiguration?.registerCard != null) { RegisterCardList( modifier = modifier.fillMaxSize().testTag(REGISTER_CARD_TEST_TAG), registerCardConfig = registerUiState.registerConfiguration.registerCard, @@ -194,7 +191,8 @@ fun RegisterScreen( onEvent = onEvent, registerUiState = registerUiState, currentPage = currentPage, - showPagination = searchQuery.value.isBlank(), + showPagination = !registerUiState.registerConfiguration.infiniteScroll && + searchQuery.value.isBlank(), onSearchByQrSingleResultAction = { resourceData -> if ( !searchQuery.value.isBlank() && searchQuery.value.mode == SearchMode.QrCodeScan diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt index d07d28d85f..3b57c75c66 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt @@ -29,8 +29,6 @@ import androidx.paging.cachedIn import androidx.paging.filter import com.google.android.fhir.sync.CurrentSyncJobStatus import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject -import kotlin.math.ceil import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -64,6 +62,8 @@ import org.smartregister.fhircore.quest.data.register.RegisterPagingSource import org.smartregister.fhircore.quest.data.register.model.RegisterPagingSourceState import org.smartregister.fhircore.quest.util.extensions.toParamDataMap import timber.log.Timber +import javax.inject.Inject +import kotlin.math.ceil @HiltViewModel class RegisterViewModel @@ -79,8 +79,7 @@ constructor( val snackBarStateFlow = _snackBarStateFlow.asSharedFlow() val registerUiState = mutableStateOf(RegisterUiState()) val currentPage: MutableState = mutableIntStateOf(0) - val paginatedRegisterData: MutableStateFlow>> = - MutableStateFlow(emptyFlow()) + val registerData: MutableStateFlow>> = MutableStateFlow(emptyFlow()) val pagesDataCache = mutableMapOf>>() val registerFilterState = mutableStateOf(RegisterFilterState()) private val _totalRecordsCount = mutableLongStateOf(0L) @@ -109,7 +108,7 @@ constructor( pagesDataCache.clear() allPatientRegisterData = null } - paginatedRegisterData.value = + registerData.value = pagesDataCache.getOrPut(currentPage.value) { getPager(registerId, loadAll).flow.cachedIn(viewModelScope) } @@ -118,7 +117,7 @@ constructor( private fun getPager(registerId: String, loadAll: Boolean = false): Pager { val currentRegisterConfigs = retrieveRegisterConfiguration(registerId) val ruleConfigs = currentRegisterConfigs.registerCard.rules - val pageSize = currentRegisterConfigs.pageSize // Default 10 + val pageSize = currentRegisterConfigs.pageSize return Pager( config = PagingConfig(pageSize = pageSize, enablePlaceholders = false), @@ -188,7 +187,7 @@ constructor( val searchBar = registerUiState.value.registerConfiguration?.searchBar // computedRules (names of pre-computed rules) must be provided for search to work. if (searchBar?.computedRules != null) { - paginatedRegisterData.value = + registerData.value = retrieveAllPatientRegisterData(registerUiState.value.registerId).map { pagingData: PagingData -> pagingData.filter { resourceData: ResourceData -> @@ -448,22 +447,24 @@ constructor( val paramsMap: Map = params.toParamDataMap() viewModelScope.launch { val currentRegisterConfiguration = retrieveRegisterConfiguration(registerId, paramsMap) + if (currentRegisterConfiguration.infiniteScroll) { + registerData.value = retrieveAllPatientRegisterData(currentRegisterConfiguration.id) + } else { + _totalRecordsCount.longValue = + registerRepository.countRegisterData(registerId = registerId, paramsMap = paramsMap) - _totalRecordsCount.longValue = - registerRepository.countRegisterData(registerId = registerId, paramsMap = paramsMap) - - // Only count filtered data when queries are updated - if (registerFilterState.value.fhirResourceConfig != null) { - _filteredRecordsCount.longValue = - registerRepository.countRegisterData( - registerId = registerId, - paramsMap = paramsMap, - fhirResourceConfig = registerFilterState.value.fhirResourceConfig, - ) + // Only count filtered data when queries are updated + if (registerFilterState.value.fhirResourceConfig != null) { + _filteredRecordsCount.longValue = + registerRepository.countRegisterData( + registerId = registerId, + paramsMap = paramsMap, + fhirResourceConfig = registerFilterState.value.fhirResourceConfig, + ) + } + paginateRegisterData(registerId = registerId, loadAll = false, clearCache = clearCache) } - paginateRegisterData(registerId, loadAll = false, clearCache = clearCache) - registerUiState.value = RegisterUiState( screenTitle = currentRegisterConfiguration.registerTitle ?: screenTitle, @@ -482,9 +483,11 @@ constructor( filteredRecordsCount = _filteredRecordsCount.longValue, pagesCount = ceil( - (if (registerFilterState.value.fhirResourceConfig != null) { - _filteredRecordsCount.longValue - } else _totalRecordsCount.longValue) + ( + if (registerFilterState.value.fhirResourceConfig != null) { + _filteredRecordsCount.longValue + } else _totalRecordsCount.longValue + ) .toDouble() .div(currentRegisterConfiguration.pageSize.toLong()), ) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/components/RegisterCardList.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/components/RegisterCardList.kt index 7cdfadc7bc..a964f7a85a 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/components/RegisterCardList.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/components/RegisterCardList.kt @@ -16,6 +16,7 @@ package org.smartregister.fhircore.quest.ui.register.components +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -46,6 +47,8 @@ import org.smartregister.fhircore.quest.ui.shared.components.ViewRenderer import timber.log.Timber const val REGISTER_CARD_LIST_TEST_TAG = "RegisterCardListTestTag" +const val PADDING_BOTTOM_WITH_FAB = 80 +const val PADDING_BOTTOM_WITHOUT_FAB = 32 /** * This is the list used to render register data. The register data is wrapped in [ResourceData] @@ -113,15 +116,22 @@ fun RegisterCardList( // Register pagination item { - if (pagingItems.itemCount > 0 && showPagination) { - RegisterFooter( - resultCount = pagingItems.itemCount, - currentPage = currentPage.value.plus(1), - pagesCount = registerUiState.pagesCount, - fabActions = registerUiState.registerConfiguration?.fabActions, - previousButtonClickListener = { onEvent(RegisterEvent.MoveToPreviousPage) }, - nextButtonClickListener = { onEvent(RegisterEvent.MoveToNextPage) }, - ) + val fabActions = registerUiState.registerConfiguration?.fabActions + Box(modifier = Modifier.padding( + bottom = + if (!fabActions.isNullOrEmpty() && fabActions.first().visible) { + PADDING_BOTTOM_WITH_FAB.dp + } else PADDING_BOTTOM_WITHOUT_FAB.dp, + )) { + if (pagingItems.itemCount > 0 && showPagination) { + RegisterFooter( + resultCount = pagingItems.itemCount, + currentPage = currentPage.value.plus(1), + pagesCount = registerUiState.pagesCount, + previousButtonClickListener = { onEvent(RegisterEvent.MoveToPreviousPage) }, + nextButtonClickListener = { onEvent(RegisterEvent.MoveToNextPage) }, + ) + } } } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt index aa349bee29..55f632705b 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt @@ -28,7 +28,6 @@ import io.mockk.mockk import io.mockk.runs import io.mockk.spyk import io.mockk.verify -import javax.inject.Inject import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.DateType @@ -60,6 +59,7 @@ import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.quest.app.fakes.Faker import org.smartregister.fhircore.quest.robolectric.RobolectricTest import org.smartregister.fhircore.quest.ui.shared.models.SearchQuery +import javax.inject.Inject @HiltAndroidTest class RegisterViewModelTest : RobolectricTest() { @@ -106,7 +106,7 @@ class RegisterViewModelTest : RobolectricTest() { pageSize = 10, ) registerViewModel.paginateRegisterData(registerId, false) - val paginatedRegisterData = registerViewModel.paginatedRegisterData.value + val paginatedRegisterData = registerViewModel.registerData.value Assert.assertNotNull(paginatedRegisterData) Assert.assertTrue(registerViewModel.pagesDataCache.isNotEmpty()) } From 398c9877616fe5b675bc07e37d37be0d8d032632 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Mon, 16 Sep 2024 21:48:46 +0300 Subject: [PATCH 050/110] Fix loading related resources This fix ensures all the nested related resources are loaded too. Signed-off-by: Elly Kitoto --- .../engine/data/local/DefaultRepository.kt | 13 +- .../data/local/register/RegisterRepository.kt | 132 +++++++++--------- 2 files changed, 75 insertions(+), 70 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 50f46b433a..ad6eb116a1 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -479,7 +479,7 @@ constructor( val relatedResourcesQueue = ArrayDeque?>>().apply { addFirst(Pair(resource, relatedResourcesConfigs)) } - while (!relatedResourcesQueue.isEmpty()) { + while (relatedResourcesQueue.isNotEmpty()) { val (currentResource, currentRelatedResourceConfigs) = relatedResourcesQueue.removeFirst() val relatedResourceCountConfigs = currentRelatedResourceConfigs ?.asSequence() @@ -525,7 +525,7 @@ constructor( } val searchResults = searchIncludedResources( - relatedResourcesConfigs = relatedResourcesConfigs, + relatedResourcesConfigs = currentRelatedResourceConfigs, resource = currentResource, configComputedRuleValues = configComputedRuleValues ) @@ -579,7 +579,7 @@ constructor( if (!id.isNullOrBlank()) { relatedResourceWrapper.relatedResourceMap[id] = relatedResourceWrapper.relatedResourceMap.getOrPut(id) { mutableListOf() }.apply { - addAll(value) + addAll(value.distinctBy { it.logicalId }) } value.forEach { resource -> with(resourceConfigs?.relatedResources) { @@ -601,7 +601,7 @@ constructor( }, ): Long = kotlin - .runCatching { withContext(dispatcherProvider.io()) { fhirEngine.count(this@count) } } + .runCatching { fhirEngine.count(this@count) } .onSuccess { count -> onSuccess(count) } .onFailure { throwable -> onFailure(throwable) } .getOrDefault(0) @@ -630,13 +630,12 @@ constructor( relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(true) search.apply { - val thisSearch = this reverseIncludeResourceConfigs?.forEach { resourceConfig -> revInclude( resourceConfig.resource, ReferenceClientParam(resourceConfig.searchParameter), ) { - thisSearch.applyConfiguredSortAndFilters( + (this as Search).applyConfiguredSortAndFilters( resourceConfig = resourceConfig, sortData = true, configComputedRuleValues = configComputedRuleValues, @@ -649,7 +648,7 @@ constructor( resourceConfig.resource, ReferenceClientParam(resourceConfig.searchParameter), ) { - thisSearch.applyConfiguredSortAndFilters( + (this as Search).applyConfiguredSortAndFilters( resourceConfig = resourceConfig, sortData = true, configComputedRuleValues = configComputedRuleValues, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt index e50a868683..0363fc2061 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt @@ -97,73 +97,79 @@ constructor( fhirResourceConfig: FhirResourceConfig?, paramsMap: Map?, ): Long { - val registerConfiguration = retrieveRegisterConfiguration(registerId, paramsMap) - val fhirResource = fhirResourceConfig ?: registerConfiguration.fhirResource - val baseResourceConfig = fhirResource.baseResource - val configComputedRuleValues = registerConfiguration.configRules.configRulesComputedValues() - val filterByRelatedEntityLocation = registerConfiguration.filterDataByRelatedEntityLocation - val filterActiveResources = registerConfiguration.activeResourceFilters - if (filterByRelatedEntityLocation) { - val syncLocationIds = context.retrieveRelatedEntitySyncLocationIds() - val locationIds = - syncLocationIds - .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId }} - .asSequence() - .flatten() - .toHashSet() - val countSearch = - Search(baseResourceConfig.resource).apply { - applyConfiguredSortAndFilters( - resourceConfig = baseResourceConfig, - sortData = false, - filterActiveResources = filterActiveResources, - configComputedRuleValues = configComputedRuleValues, - ) - } - val totalCount = fhirEngine.count(countSearch) - var searchResultsCount = 0L - var pageNumber = 0 - var count = 0 - while (count < totalCount) { - val baseResourceSearch = - createSearch( - baseResourceConfig = baseResourceConfig, - filterActiveResources = filterActiveResources, - configComputedRuleValues = configComputedRuleValues, - currentPage = pageNumber, - count = COUNT, - ) - searchResultsCount += fhirEngine.search(baseResourceSearch) - .asSequence() - .map { it.resource } - .filter { resource -> - when (resource.resourceType) { - ResourceType.Location -> locationIds.contains(resource.logicalId) - else -> resource.meta.tag.any { - it.system == context.getString(R.string.sync_strategy_related_entity_location_system) - && locationIds.contains(it.code) + return withContext(dispatcherProvider.io()) { + val registerConfiguration = retrieveRegisterConfiguration(registerId, paramsMap) + val fhirResource = fhirResourceConfig ?: registerConfiguration.fhirResource + val baseResourceConfig = fhirResource.baseResource + val configComputedRuleValues = registerConfiguration.configRules.configRulesComputedValues() + val filterByRelatedEntityLocation = registerConfiguration.filterDataByRelatedEntityLocation + val filterActiveResources = registerConfiguration.activeResourceFilters + if (filterByRelatedEntityLocation) { + val syncLocationIds = context.retrieveRelatedEntitySyncLocationIds() + val locationIds = + syncLocationIds + .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId } } + .asSequence() + .flatten() + .toHashSet() + val countSearch = + Search(baseResourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = baseResourceConfig, + sortData = false, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + ) + } + val totalCount = fhirEngine.count(countSearch) + var searchResultsCount = 0L + var pageNumber = 0 + var count = 0 + while (count < totalCount) { + val baseResourceSearch = + createSearch( + baseResourceConfig = baseResourceConfig, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + currentPage = pageNumber, + count = COUNT, + ) + searchResultsCount += fhirEngine.search(baseResourceSearch) + .asSequence() + .map { it.resource } + .filter { resource -> + when (resource.resourceType) { + ResourceType.Location -> locationIds.contains(resource.logicalId) + else -> resource.meta.tag.any { + it.system == context.getString(R.string.sync_strategy_related_entity_location_system) + && locationIds.contains(it.code) + } } + }.count().toLong() + count += COUNT + pageNumber++ + } + searchResultsCount + } else { + val search = + Search(baseResourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = baseResourceConfig, + sortData = false, + filterActiveResources = registerConfiguration.activeResourceFilters, + configComputedRuleValues = configComputedRuleValues, + ) } - }.count().toLong() - count += COUNT - pageNumber++ - } - return searchResultsCount - } - val search = - Search(baseResourceConfig.resource).apply { - applyConfiguredSortAndFilters( - resourceConfig = baseResourceConfig, - sortData = false, - filterActiveResources = registerConfiguration.activeResourceFilters, - configComputedRuleValues = configComputedRuleValues, + search.count( + onFailure = { + Timber.e( + it, + "Error counting register data for register id: ${registerConfiguration.id}" + ) + }, ) } - return search.count( - onFailure = { - Timber.e(it, "Error counting register data for register id: ${registerConfiguration.id}") - }, - ) + } } override suspend fun loadProfileData( From aca51d7422267b2d322dc616be39b76a6198ef9a Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Mon, 16 Sep 2024 22:55:48 +0300 Subject: [PATCH 051/110] Batch related resource queries Signed-off-by: Elly Kitoto --- .../engine/data/local/DefaultRepository.kt | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index ad6eb116a1..1d6143c1cb 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -32,6 +32,8 @@ import com.google.android.fhir.db.ResourceNotFoundException import com.google.android.fhir.get import com.google.android.fhir.search.Order import com.google.android.fhir.search.Search +import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion +import com.google.android.fhir.search.filter.TokenParamFilterCriterion import com.google.android.fhir.search.has import com.google.android.fhir.search.include import com.google.android.fhir.search.revInclude @@ -476,11 +478,11 @@ constructor( configComputedRuleValues: Map, ): RelatedResourceWrapper { val relatedResourceWrapper = RelatedResourceWrapper() - val relatedResourcesQueue = ArrayDeque?>>().apply { - addFirst(Pair(resource, relatedResourcesConfigs)) + val relatedResourcesQueue = ArrayDeque, List?>>().apply { + addFirst(Pair(listOf(resource), relatedResourcesConfigs)) } while (relatedResourcesQueue.isNotEmpty()) { - val (currentResource, currentRelatedResourceConfigs) = relatedResourcesQueue.removeFirst() + val (currentResources, currentRelatedResourceConfigs) = relatedResourcesQueue.removeFirst() val relatedResourceCountConfigs = currentRelatedResourceConfigs ?.asSequence() ?.filter { it.resultAsCount && !it.searchParameter.isNullOrEmpty() } @@ -488,9 +490,16 @@ constructor( relatedResourceCountConfigs?.forEach { resourceConfig -> val search = Search(resourceConfig.resource).apply { + val filters = + currentResources.map { + val apply: ReferenceParamFilterCriterion.() -> Unit = { + value = it.logicalId.asReference(it.resourceType).reference + } + apply + } filter( ReferenceClientParam(resourceConfig.searchParameter), - { value = currentResource.asReference().reference } + *filters.toTypedArray(), ) applyConfiguredSortAndFilters( resourceConfig = resourceConfig, @@ -526,7 +535,7 @@ constructor( val searchResults = searchIncludedResources( relatedResourcesConfigs = currentRelatedResourceConfigs, - resource = currentResource, + resources = currentResources, configComputedRuleValues = configComputedRuleValues ) @@ -544,7 +553,7 @@ constructor( updateResourceWrapperAndQueue( key = entry.key, defaultKey = entry.value.firstOrNull()?.resourceType?.name, - value = entry.value, + resources = entry.value, relatedResourcesConfigsMap = fwdIncludedRelatedConfigsMap, relatedResourceWrapper = relatedResourceWrapper, relatedResourcesQueue = relatedResourcesQueue @@ -556,7 +565,7 @@ constructor( updateResourceWrapperAndQueue( key = key, defaultKey = entry.value.firstOrNull()?.resourceType?.name, - value = entry.value, + resources = entry.value, relatedResourcesConfigsMap = revIncludedRelatedConfigsMap, relatedResourceWrapper = relatedResourceWrapper, relatedResourcesQueue = relatedResourcesQueue @@ -570,21 +579,21 @@ constructor( private fun updateResourceWrapperAndQueue( key: String, defaultKey: String?, - value: List, + resources: List, relatedResourcesConfigsMap: Map?, relatedResourceWrapper: RelatedResourceWrapper, - relatedResourcesQueue: ArrayDeque?>>) { + relatedResourcesQueue: ArrayDeque, List?>>) { val resourceConfigs = relatedResourcesConfigsMap?.get(key) val id = resourceConfigs?.id ?: defaultKey if (!id.isNullOrBlank()) { relatedResourceWrapper.relatedResourceMap[id] = relatedResourceWrapper.relatedResourceMap.getOrPut(id) { mutableListOf() }.apply { - addAll(value.distinctBy { it.logicalId }) + addAll(resources.distinctBy { it.logicalId }) } - value.forEach { resource -> + resources.chunked(COUNT) { item -> with(resourceConfigs?.relatedResources) { if (!this.isNullOrEmpty()) { - relatedResourcesQueue.addLast(Pair(resource, this)) + relatedResourcesQueue.addLast(Pair(item, this)) } } } @@ -613,12 +622,17 @@ constructor( */ private suspend fun searchIncludedResources( relatedResourcesConfigs: List?, - resource: Resource, + resources: List, configComputedRuleValues: Map, ): List> { val search = - Search(resource.resourceType).apply { - filter(Resource.RES_ID, { value = of(resource.logicalId) }) + Search(resources.first().resourceType).apply { + val filters = + resources.map { + val apply: TokenParamFilterCriterion.() -> Unit = { value = of(it.logicalId) } + apply + } + filter(Resource.RES_ID, *filters.toTypedArray()) } // Forward include related resources e.g. a member or managingEntity of a Group resource From 2e3e48ca0f321e520150b11c4d5cecad767175df Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Mon, 16 Sep 2024 23:28:09 +0300 Subject: [PATCH 052/110] Map resources to RepositoryResourceData with async map Signed-off-by: Elly Kitoto --- .../fhircore/engine/data/local/DefaultRepository.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 1d6143c1cb..7751bd9487 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -93,6 +93,7 @@ import org.smartregister.fhircore.engine.util.extension.retrieveRelatedEntitySyn import org.smartregister.fhircore.engine.util.extension.updateFrom import org.smartregister.fhircore.engine.util.extension.updateLastUpdated import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor +import org.smartregister.fhircore.engine.util.pmap import timber.log.Timber import java.util.UUID import javax.inject.Inject @@ -997,7 +998,7 @@ constructor( filterActiveResources = filterActiveResources, baseResourceConfig = baseResourceConfig, ) - } + } as List } } @@ -1008,7 +1009,7 @@ constructor( filterActiveResources: List?, baseResourceConfig: ResourceConfig, ) = - this.map { searchResult -> + this.pmap { searchResult -> val retrievedRelatedResources = retrieveRelatedResources( resource = searchResult.resource, From 2eb9c85217eec61dd5c5806e5c0a6b6a8b5ae348 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Tue, 17 Sep 2024 00:32:03 +0300 Subject: [PATCH 053/110] Make infinite scroll the default register behavior Signed-off-by: Elly Kitoto --- .../register/RegisterConfiguration.kt | 2 +- .../quest/ui/register/RegisterFragment.kt | 4 +-- .../quest/ui/register/RegisterViewModel.kt | 36 +++++++++++-------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt index d96d3e657a..732bd36d5d 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt @@ -50,7 +50,7 @@ data class RegisterConfiguration( val filterDataByRelatedEntityLocation: Boolean = false, val topScreenSection: TopScreenSectionConfig? = null, val onSearchByQrSingleResultActions: List? = null, - val infiniteScroll: Boolean = false + val infiniteScroll: Boolean = true ) : Configuration() { val onSearchByQrSingleResultValidActions = onSearchByQrSingleResultActions?.filter { it.trigger == ActionTrigger.ON_SEARCH_SINGLE_RESULT } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt index eb9b04d65e..601d529406 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt @@ -228,13 +228,11 @@ class RegisterFragment : Fragment(), OnSyncListener { updateRegisterFilterState(registerId, questionnaireResponse) } - pagesDataCache.clear() - retrieveRegisterUiState( registerId = registerId, screenTitle = screenTitle, params = params, - clearCache = false, + clearCache = true, ) } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt index 3b57c75c66..66834c58c0 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt @@ -85,7 +85,7 @@ constructor( private val _totalRecordsCount = mutableLongStateOf(0L) private val _filteredRecordsCount = mutableLongStateOf(-1L) private lateinit var registerConfiguration: RegisterConfiguration - private var allPatientRegisterData: Flow>? = null + private var completeRegisterData: Flow>? = null private val _percentageProgress: MutableSharedFlow = MutableSharedFlow(0) private val _isUploadSync: MutableSharedFlow = MutableSharedFlow(0) private val _currentSyncJobStatusFlow: MutableSharedFlow = @@ -106,7 +106,7 @@ constructor( ) { if (clearCache) { pagesDataCache.clear() - allPatientRegisterData = null + completeRegisterData = null } registerData.value = pagesDataCache.getOrPut(currentPage.value) { @@ -154,41 +154,49 @@ constructor( return registerConfiguration } - private fun retrieveAllPatientRegisterData(registerId: String): Flow> { - // Ensure that we only initialize this flow once - if (allPatientRegisterData == null) { - allPatientRegisterData = getPager(registerId, true).flow.cachedIn(viewModelScope) + private fun retrieveCompleteRegisterData(registerId: String, forceRefresh: Boolean): + Flow> { + if (completeRegisterData == null || forceRefresh) { + completeRegisterData = getPager(registerId, true).flow.cachedIn(viewModelScope) } - return allPatientRegisterData!! + return completeRegisterData!! } - fun onEvent(event: RegisterEvent) = + fun onEvent(event: RegisterEvent) { + val registerId = registerUiState.value.registerId when (event) { // Search using name or patient logicalId or identifier. Modify to add more search params is RegisterEvent.SearchRegister -> { if (event.searchQuery.isBlank()) { - paginateRegisterData(registerUiState.value.registerId) + val regConfig = retrieveRegisterConfiguration(registerId) + if (regConfig.infiniteScroll) { + registerData.value = retrieveCompleteRegisterData(registerId, false) + } else paginateRegisterData(registerId) } else { filterRegisterData(event.searchQuery.query) } } + is RegisterEvent.MoveToNextPage -> { currentPage.value = currentPage.value.plus(1) - paginateRegisterData(registerUiState.value.registerId) + paginateRegisterData(registerId) } + is RegisterEvent.MoveToPreviousPage -> { currentPage.value.let { if (it > 0) currentPage.value = it.minus(1) } - paginateRegisterData(registerUiState.value.registerId) + paginateRegisterData(registerId) } + RegisterEvent.ResetFilterRecordsCount -> _filteredRecordsCount.longValue = -1 - } + } + } fun filterRegisterData(searchText: String) { val searchBar = registerUiState.value.registerConfiguration?.searchBar // computedRules (names of pre-computed rules) must be provided for search to work. if (searchBar?.computedRules != null) { registerData.value = - retrieveAllPatientRegisterData(registerUiState.value.registerId).map { + retrieveCompleteRegisterData(registerUiState.value.registerId, false).map { pagingData: PagingData -> pagingData.filter { resourceData: ResourceData -> searchBar.computedRules!!.any { ruleName -> @@ -448,7 +456,7 @@ constructor( viewModelScope.launch { val currentRegisterConfiguration = retrieveRegisterConfiguration(registerId, paramsMap) if (currentRegisterConfiguration.infiniteScroll) { - registerData.value = retrieveAllPatientRegisterData(currentRegisterConfiguration.id) + registerData.value = retrieveCompleteRegisterData(currentRegisterConfiguration.id, clearCache) } else { _totalRecordsCount.longValue = registerRepository.countRegisterData(registerId = registerId, paramsMap = paramsMap) From b3f838acb6f67f4fd12d277e3d1ede45a0a49250 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Tue, 17 Sep 2024 01:18:09 +0300 Subject: [PATCH 054/110] Disable automatic intialization of emoji2 A lot of memory was used in heap during the allocation. Emojis are not used in the app so intializing them automatically is unnecessary. Signed-off-by: Elly Kitoto --- android/quest/src/main/AndroidManifest.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/android/quest/src/main/AndroidManifest.xml b/android/quest/src/main/AndroidManifest.xml index ff169b7773..22a965498f 100644 --- a/android/quest/src/main/AndroidManifest.xml +++ b/android/quest/src/main/AndroidManifest.xml @@ -94,5 +94,13 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> + + + From 77d16628c0b9a8555f6eca84dafb27396f38c280 Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Tue, 17 Sep 2024 17:30:22 +0300 Subject: [PATCH 055/110] Run spotlessApply Signed-off-by: Elly Kitoto --- android/engine/build.gradle.kts | 4 +- .../configuration/ConfigurationRegistry.kt | 29 +- .../navigation/NavigationMenuConfig.kt | 1 - .../register/RegisterConfiguration.kt | 2 +- .../configuration/view/ButtonProperties.kt | 4 +- .../configuration/view/ViewProperties.kt | 3 +- .../view/ViewPropertiesSerializer.kt | 4 +- .../engine/data/local/DefaultRepository.kt | 318 +++++++++--------- .../data/local/register/RegisterRepository.kt | 30 +- .../data/remote/shared/TokenAuthenticator.kt | 4 +- .../fhircore/engine/di/NetworkModule.kt | 8 +- .../fhircore/engine/pdf/HtmlPopulator.kt | 4 +- .../engine/rulesengine/ConfigRulesExecutor.kt | 4 +- .../rulesengine/ResourceDataRulesExecutor.kt | 4 +- .../engine/rulesengine/RulesFactory.kt | 11 +- .../engine/task/FhirCarePlanGenerator.kt | 4 +- .../fhircore/engine/ui/base/AlertDialogue.kt | 4 +- .../fhircore/engine/ui/components/Pin.kt | 4 +- .../ui/components/register/RegisterFooter.kt | 1 - .../engine/ui/multiselect/MultiSelectView.kt | 4 +- .../util/extension/AndroidExtensions.kt | 6 +- .../util/extension/MeasureExtensions.kt | 3 +- .../util/extension/QuestionnaireExtension.kt | 4 +- .../util/extension/ReferenceExtension.kt | 3 +- .../util/extension/ResourceExtension.kt | 12 +- .../geowidget/screens/GeoWidgetViewModel.kt | 2 +- .../fhircore/quest/data/DataMigration.kt | 4 +- .../data/register/RegisterPagingSource.kt | 7 +- .../ui/appsetting/AppSettingViewModel.kt | 3 +- .../fhircore/quest/ui/login/LoginActivity.kt | 4 +- .../fhircore/quest/ui/main/AppMainActivity.kt | 4 +- .../quest/ui/main/AppMainViewModel.kt | 31 +- .../quest/ui/main/components/AppDrawer.kt | 8 +- .../ui/multiselect/MultiSelectViewModel.kt | 2 +- .../quest/ui/profile/ProfileScreen.kt | 4 +- .../quest/ui/profile/ProfileViewModel.kt | 4 +- .../components/ChangeManagingEntityView.kt | 8 +- .../questionnaire/QuestionnaireViewModel.kt | 8 +- .../quest/ui/register/RegisterFragment.kt | 2 +- .../quest/ui/register/RegisterScreen.kt | 9 +- .../quest/ui/register/RegisterViewModel.kt | 32 +- .../register/components/RegisterCardList.kt | 17 +- .../EditTextQrCodeItemViewHolderFactory.kt | 3 +- .../qrCode/EditTextQrCodeViewHolderFactory.kt | 4 +- .../ui/shared/components/ActionableButton.kt | 4 +- .../ui/shared/components/SyncStatusView.kt | 16 +- .../ui/shared/components/ViewRenderer.kt | 4 +- .../quest/util/extensions/ConfigExtensions.kt | 27 +- .../fhircore/quest/CqlContentTest.kt | 4 +- .../ui/register/RegisterViewModelTest.kt | 2 +- .../util/extensions/ConfigExtensionsKtTest.kt | 14 +- 51 files changed, 408 insertions(+), 294 deletions(-) diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index eecbfb2b87..6616981e4a 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -176,9 +176,7 @@ dependencies { api(libs.timber) api(libs.converter.gson) api(libs.json.path) - api(libs.easy.rules.jexl) { - exclude(group = "commons-logging", module = "commons-logging") - } + api(libs.easy.rules.jexl) { exclude(group = "commons-logging", module = "commons-logging") } api(libs.data.capture) { isTransitive = true exclude(group = "ca.uhn.hapi.fhir") diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index a7106e4bf0..82c126faf4 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -29,6 +29,15 @@ import com.google.android.fhir.get import com.google.android.fhir.knowledge.KnowledgeManager import com.google.android.fhir.sync.download.ResourceSearchParams import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File +import java.io.FileNotFoundException +import java.io.InputStreamReader +import java.net.UnknownHostException +import java.util.Locale +import java.util.PropertyResourceBundle +import java.util.ResourceBundle +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import okhttp3.RequestBody.Companion.toRequestBody @@ -71,15 +80,6 @@ import org.smartregister.fhircore.engine.util.extension.updateLastUpdated import org.smartregister.fhircore.engine.util.helper.LocalizationHelper import retrofit2.HttpException import timber.log.Timber -import java.io.File -import java.io.FileNotFoundException -import java.io.InputStreamReader -import java.net.UnknownHostException -import java.util.Locale -import java.util.PropertyResourceBundle -import java.util.ResourceBundle -import javax.inject.Inject -import javax.inject.Singleton @Singleton class ConfigurationRegistry @@ -378,14 +378,18 @@ constructor( context.assets.list(String.format(BASE_CONFIG_PATH, appId))?.onEach { if (!supportedFileExtensions.contains(it.fileExtension)) { filesQueue.addLast(String.format(BASE_CONFIG_PATH, appId) + "/$it") - } else configFiles.add(String.format(BASE_CONFIG_PATH, appId) + "/$it") + } else { + configFiles.add(String.format(BASE_CONFIG_PATH, appId) + "/$it") + } } while (filesQueue.isNotEmpty()) { val currentPath = filesQueue.removeFirst() context.assets.list(currentPath)?.onEach { if (!supportedFileExtensions.contains(it.fileExtension)) { filesQueue.addLast("$currentPath/$it") - } else configFiles.add("$currentPath/$it") + } else { + configFiles.add("$currentPath/$it") + } } } return configFiles @@ -507,13 +511,14 @@ constructor( val resultBundle = if (isNonProxy()) { fhirResourceDataSourceGetBundle(resourceType, resourceIdList) - } else + } else { fhirResourceDataSource.post( requestBody = generateRequestBundle(resourceType, resourceIdList) .encodeResourceToString() .toRequestBody(NetworkModule.JSON_MEDIA_TYPE), ) + } processResultBundleEntries(resultBundle.entry) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/navigation/NavigationMenuConfig.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/navigation/NavigationMenuConfig.kt index 9cb66a6e51..f545c4481e 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/navigation/NavigationMenuConfig.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/navigation/NavigationMenuConfig.kt @@ -22,7 +22,6 @@ import kotlinx.serialization.Serializable import org.smartregister.fhircore.engine.domain.model.ActionConfig import org.smartregister.fhircore.engine.util.extension.interpolate - const val ICON_TYPE_LOCAL = "local" const val ICON_TYPE_REMOTE = "remote" diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt index 732bd36d5d..b4a82d4cbb 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt @@ -50,7 +50,7 @@ data class RegisterConfiguration( val filterDataByRelatedEntityLocation: Boolean = false, val topScreenSection: TopScreenSectionConfig? = null, val onSearchByQrSingleResultActions: List? = null, - val infiniteScroll: Boolean = true + val infiniteScroll: Boolean = true, ) : Configuration() { val onSearchByQrSingleResultValidActions = onSearchByQrSingleResultActions?.filter { it.trigger == ActionTrigger.ON_SEARCH_SINGLE_RESULT } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ButtonProperties.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ButtonProperties.kt index 8996c628f5..5e89b02a09 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ButtonProperties.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ButtonProperties.kt @@ -91,7 +91,9 @@ data class ButtonProperties( val interpolated = this.status.interpolate(computedValuesMap) return if (ServiceStatus.values().map { it.name }.contains(interpolated)) { ServiceStatus.valueOf(interpolated) - } else ServiceStatus.UPCOMING + } else { + ServiceStatus.UPCOMING + } } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewProperties.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewProperties.kt index ba9dd121b1..30c1059b21 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewProperties.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewProperties.kt @@ -56,8 +56,7 @@ fun List.retrieveListProperties(): List { ViewType.COLUMN -> viewPropertiesQueue.addAll((properties as ColumnProperties).children) ViewType.ROW -> viewPropertiesQueue.addAll((properties as RowProperties).children) ViewType.CARD -> viewPropertiesQueue.addAll((properties as CardViewProperties).content) - ViewType.LIST -> - viewPropertiesQueue.addAll((properties as ListProperties).registerCard.views) + ViewType.LIST -> viewPropertiesQueue.addAll((properties as ListProperties).registerCard.views) else -> {} } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewPropertiesSerializer.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewPropertiesSerializer.kt index 6574ed470f..7c3a47ab6f 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewPropertiesSerializer.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/view/ViewPropertiesSerializer.kt @@ -32,7 +32,9 @@ object ViewPropertiesSerializer : ): DeserializationStrategy { val jsonObject = element.jsonObject val viewType = jsonObject[VIEW_TYPE]?.jsonPrimitive?.content - require(viewType != null && ViewType.entries.toTypedArray().contains(ViewType.valueOf(viewType))) { + require( + viewType != null && ViewType.entries.toTypedArray().contains(ViewType.valueOf(viewType)), + ) { """Ensure that supported `viewType` property is included in your register view properties configuration. Supported types: ${ViewType.entries.toTypedArray()} Parsed JSON: $jsonObject diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index 7751bd9487..c6e1c3f66f 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -479,73 +479,81 @@ constructor( configComputedRuleValues: Map, ): RelatedResourceWrapper { val relatedResourceWrapper = RelatedResourceWrapper() - val relatedResourcesQueue = ArrayDeque, List?>>().apply { - addFirst(Pair(listOf(resource), relatedResourcesConfigs)) - } + val relatedResourcesQueue = + ArrayDeque, List?>>().apply { + addFirst(Pair(listOf(resource), relatedResourcesConfigs)) + } while (relatedResourcesQueue.isNotEmpty()) { val (currentResources, currentRelatedResourceConfigs) = relatedResourcesQueue.removeFirst() - val relatedResourceCountConfigs = currentRelatedResourceConfigs - ?.asSequence() - ?.filter { it.resultAsCount && !it.searchParameter.isNullOrEmpty() } - ?.toList() + val relatedResourceCountConfigs = + currentRelatedResourceConfigs + ?.asSequence() + ?.filter { it.resultAsCount && !it.searchParameter.isNullOrEmpty() } + ?.toList() relatedResourceCountConfigs?.forEach { resourceConfig -> - val search = Search(resourceConfig.resource).apply { - val filters = - currentResources.map { - val apply: ReferenceParamFilterCriterion.() -> Unit = { - value = it.logicalId.asReference(it.resourceType).reference + val search = + Search(resourceConfig.resource).apply { + val filters = + currentResources.map { + val apply: ReferenceParamFilterCriterion.() -> Unit = { + value = it.logicalId.asReference(it.resourceType).reference + } + apply } - apply - } - filter( - ReferenceClientParam(resourceConfig.searchParameter), - *filters.toTypedArray(), - ) - applyConfiguredSortAndFilters( - resourceConfig = resourceConfig, - sortData = true, - configComputedRuleValues = configComputedRuleValues - ) - } + filter( + ReferenceClientParam(resourceConfig.searchParameter), + *filters.toTypedArray(), + ) + applyConfiguredSortAndFilters( + resourceConfig = resourceConfig, + sortData = true, + configComputedRuleValues = configComputedRuleValues, + ) + } val key = resourceConfig.id ?: resourceConfig.resource.name if (resourceConfig.countResultConfig?.sumCounts == true) { search.count( onSuccess = { - relatedResourceWrapper.relatedResourceCountMap.getOrPut(key) { mutableListOf()} - .apply { + relatedResourceWrapper.relatedResourceCountMap + .getOrPut(key) { mutableListOf() } + .apply { add( RelatedResourceCount( - count = it, - relatedResourceType = resourceConfig.resource, - parentResourceId = resource.logicalId - ) + count = it, + relatedResourceType = resourceConfig.resource, + parentResourceId = resource.logicalId, + ), ) } }, onFailure = { Timber.e( it, - "Error retrieving total count for all related resources identified by $key" + "Error retrieving total count for all related resources identified by $key", ) - } + }, ) } } - val searchResults = searchIncludedResources( - relatedResourcesConfigs = currentRelatedResourceConfigs, - resources = currentResources, - configComputedRuleValues = configComputedRuleValues - ) + val searchResults = + searchIncludedResources( + relatedResourcesConfigs = currentRelatedResourceConfigs, + resources = currentResources, + configComputedRuleValues = configComputedRuleValues, + ) val fwdIncludedRelatedConfigsMap = - currentRelatedResourceConfigs?.revIncludeRelatedResourceConfigs(false) - ?.groupBy { it.searchParameter!! }?.mapValues { it.value.first() } + currentRelatedResourceConfigs + ?.revIncludeRelatedResourceConfigs(false) + ?.groupBy { it.searchParameter!! } + ?.mapValues { it.value.first() } val revIncludedRelatedConfigsMap = - currentRelatedResourceConfigs?.revIncludeRelatedResourceConfigs(true) + currentRelatedResourceConfigs + ?.revIncludeRelatedResourceConfigs(true) ?.groupBy { "${it.resource.name}_${it.searchParameter}".lowercase() } ?.mapValues { it.value.first() } @@ -557,7 +565,7 @@ constructor( resources = entry.value, relatedResourcesConfigsMap = fwdIncludedRelatedConfigsMap, relatedResourceWrapper = relatedResourceWrapper, - relatedResourcesQueue = relatedResourcesQueue + relatedResourcesQueue = relatedResourcesQueue, ) } searchResult.revIncluded?.forEach { entry -> @@ -569,7 +577,7 @@ constructor( resources = entry.value, relatedResourcesConfigsMap = revIncludedRelatedConfigsMap, relatedResourceWrapper = relatedResourceWrapper, - relatedResourcesQueue = relatedResourcesQueue + relatedResourcesQueue = relatedResourcesQueue, ) } } @@ -583,22 +591,23 @@ constructor( resources: List, relatedResourcesConfigsMap: Map?, relatedResourceWrapper: RelatedResourceWrapper, - relatedResourcesQueue: ArrayDeque, List?>>) { - val resourceConfigs = relatedResourcesConfigsMap?.get(key) - val id = resourceConfigs?.id ?: defaultKey - if (!id.isNullOrBlank()) { - relatedResourceWrapper.relatedResourceMap[id] = - relatedResourceWrapper.relatedResourceMap.getOrPut(id) { mutableListOf() }.apply { - addAll(resources.distinctBy { it.logicalId }) - } - resources.chunked(COUNT) { item -> - with(resourceConfigs?.relatedResources) { - if (!this.isNullOrEmpty()) { - relatedResourcesQueue.addLast(Pair(item, this)) - } + relatedResourcesQueue: ArrayDeque, List?>>, + ) { + val resourceConfigs = relatedResourcesConfigsMap?.get(key) + val id = resourceConfigs?.id ?: defaultKey + if (!id.isNullOrBlank()) { + relatedResourceWrapper.relatedResourceMap[id] = + relatedResourceWrapper.relatedResourceMap + .getOrPut(id) { mutableListOf() } + .apply { addAll(resources.distinctBy { it.logicalId }) } + resources.chunked(COUNT) { item -> + with(resourceConfigs?.relatedResources) { + if (!this.isNullOrEmpty()) { + relatedResourcesQueue.addLast(Pair(item, this)) } } } + } } protected suspend fun Search.count( @@ -626,55 +635,55 @@ constructor( resources: List, configComputedRuleValues: Map, ): List> { - val search = - Search(resources.first().resourceType).apply { - val filters = - resources.map { - val apply: TokenParamFilterCriterion.() -> Unit = { value = of(it.logicalId) } - apply - } - filter(Resource.RES_ID, *filters.toTypedArray()) - } - - // Forward include related resources e.g. a member or managingEntity of a Group resource - val forwardIncludeResourceConfigs = - relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(false) - - // Reverse include related resources e.g. all CarePlans, Immunizations for Patient resource - val reverseIncludeResourceConfigs = - relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(true) - - search.apply { - reverseIncludeResourceConfigs?.forEach { resourceConfig -> - revInclude( - resourceConfig.resource, - ReferenceClientParam(resourceConfig.searchParameter), - ) { - (this as Search).applyConfiguredSortAndFilters( - resourceConfig = resourceConfig, - sortData = true, - configComputedRuleValues = configComputedRuleValues, - ) + val search = + Search(resources.first().resourceType).apply { + val filters = + resources.map { + val apply: TokenParamFilterCriterion.() -> Unit = { value = of(it.logicalId) } + apply } + filter(Resource.RES_ID, *filters.toTypedArray()) + } + + // Forward include related resources e.g. a member or managingEntity of a Group resource + val forwardIncludeResourceConfigs = + relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(false) + + // Reverse include related resources e.g. all CarePlans, Immunizations for Patient resource + val reverseIncludeResourceConfigs = + relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(true) + + search.apply { + reverseIncludeResourceConfigs?.forEach { resourceConfig -> + revInclude( + resourceConfig.resource, + ReferenceClientParam(resourceConfig.searchParameter), + ) { + (this as Search).applyConfiguredSortAndFilters( + resourceConfig = resourceConfig, + sortData = true, + configComputedRuleValues = configComputedRuleValues, + ) } + } - forwardIncludeResourceConfigs?.forEach { resourceConfig -> - include( - resourceConfig.resource, - ReferenceClientParam(resourceConfig.searchParameter), - ) { - (this as Search).applyConfiguredSortAndFilters( - resourceConfig = resourceConfig, - sortData = true, - configComputedRuleValues = configComputedRuleValues, - ) - } + forwardIncludeResourceConfigs?.forEach { resourceConfig -> + include( + resourceConfig.resource, + ReferenceClientParam(resourceConfig.searchParameter), + ) { + (this as Search).applyConfiguredSortAndFilters( + resourceConfig = resourceConfig, + sortData = true, + configComputedRuleValues = configComputedRuleValues, + ) } } - return kotlin + } + return kotlin .runCatching { fhirEngine.search(search) } - .onFailure { Timber.e(it, "Error fetching related resources") } - .getOrDefault(emptyList()) + .onFailure { Timber.e(it, "Error fetching related resources") } + .getOrDefault(emptyList()) } private fun List.revIncludeRelatedResourceConfigs(isRevInclude: Boolean) = @@ -731,11 +740,12 @@ constructor( } resources.forEach { resource -> - val retrievedRelatedResources = retrieveRelatedResources( - resource = resource, - relatedResourcesConfigs = resourceConfig.relatedResources, - configComputedRuleValues = computedValuesMap, - ) + val retrievedRelatedResources = + retrieveRelatedResources( + resource = resource, + relatedResourcesConfigs = resourceConfig.relatedResources, + configComputedRuleValues = computedValuesMap, + ) retrievedRelatedResources.relatedResourceMap.forEach { resourcesMap -> val filteredRelatedResources = filterResourcesByFhirPathExpression( @@ -906,48 +916,52 @@ constructor( val syncLocationIds = context.retrieveRelatedEntitySyncLocationIds() val locationIds = syncLocationIds - .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId }} + .map { retrieveFlattenedSubLocations(it).map { subLocation -> subLocation.logicalId } } .flatten() .toHashSet() - val countSearch = - Search(baseResourceConfig.resource).apply { - applyConfiguredSortAndFilters( - resourceConfig = baseResourceConfig, - sortData = false, - filterActiveResources = filterActiveResources, - configComputedRuleValues = configComputedRuleValues, - ) - } - val totalCount = fhirEngine.count(countSearch) - val searchResults = ArrayDeque>() - var pageNumber = 0 - var count = 0 - while (count < totalCount) { - val baseResourceSearch = - createSearch( - baseResourceConfig = baseResourceConfig, - filterActiveResources = filterActiveResources, - configComputedRuleValues = configComputedRuleValues, - currentPage = pageNumber, - count = COUNT, - ) - val result = fhirEngine.search(baseResourceSearch) - searchResults.addAll(result.filter { searchResult -> + val countSearch = + Search(baseResourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = baseResourceConfig, + sortData = false, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + ) + } + val totalCount = fhirEngine.count(countSearch) + val searchResults = ArrayDeque>() + var pageNumber = 0 + var count = 0 + while (count < totalCount) { + val baseResourceSearch = + createSearch( + baseResourceConfig = baseResourceConfig, + filterActiveResources = filterActiveResources, + configComputedRuleValues = configComputedRuleValues, + currentPage = pageNumber, + count = COUNT, + ) + val result = fhirEngine.search(baseResourceSearch) + searchResults.addAll( + result.filter { searchResult -> when (baseResourceConfig.resource) { - ResourceType.Location -> locationIds.contains(searchResult.resource.logicalId) - else -> searchResult.resource.meta.tag.any { - it.system == context.getString(R.string.sync_strategy_related_entity_location_system) - && locationIds.contains(it.code) + ResourceType.Location -> locationIds.contains(searchResult.resource.logicalId) + else -> + searchResult.resource.meta.tag.any { + it.system == + context.getString(R.string.sync_strategy_related_entity_location_system) && + locationIds.contains(it.code) } } - }) - count += COUNT - pageNumber++ - if (currentPage != null && pageSize != null) { - val maxPageCount = (currentPage + 1) * pageSize - if (searchResults.size >= maxPageCount) break - } + }, + ) + count += COUNT + pageNumber++ + if (currentPage != null && pageSize != null) { + val maxPageCount = (currentPage + 1) * pageSize + if (searchResults.size >= maxPageCount) break } + } if (currentPage != null && pageSize != null) { val fromIndex = currentPage * pageSize @@ -998,7 +1012,8 @@ constructor( filterActiveResources = filterActiveResources, baseResourceConfig = baseResourceConfig, ) - } as List + } + as List } } @@ -1132,16 +1147,16 @@ constructor( } private suspend fun retrieveSubLocations(locationId: String): ArrayDeque = - fhirEngine - .search( - Search(type = ResourceType.Location).apply { - filter( - Location.PARTOF, - { value = locationId.asReference(ResourceType.Location).reference }, - ) - }, - ) - .mapTo(ArrayDeque()) { it.resource } + fhirEngine + .search( + Search(type = ResourceType.Location).apply { + filter( + Location.PARTOF, + { value = locationId.asReference(ResourceType.Location).reference }, + ) + }, + ) + .mapTo(ArrayDeque()) { it.resource } /** * A wrapper data class to hold search results. All related resources are flattened into one Map @@ -1149,7 +1164,8 @@ constructor( */ data class RelatedResourceWrapper( val relatedResourceMap: MutableMap> = mutableMapOf(), - val relatedResourceCountMap: MutableMap> = mutableMapOf(), + val relatedResourceCountMap: MutableMap> = + mutableMapOf(), ) companion object { diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt index 0363fc2061..06137e66be 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt @@ -22,6 +22,7 @@ import com.google.android.fhir.FhirEngine import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.search.Search import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.ResourceType @@ -44,7 +45,6 @@ import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.extension.retrieveRelatedEntitySyncLocationIds import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import timber.log.Timber -import javax.inject.Inject class RegisterRepository @Inject @@ -134,18 +134,24 @@ constructor( currentPage = pageNumber, count = COUNT, ) - searchResultsCount += fhirEngine.search(baseResourceSearch) - .asSequence() - .map { it.resource } - .filter { resource -> - when (resource.resourceType) { - ResourceType.Location -> locationIds.contains(resource.logicalId) - else -> resource.meta.tag.any { - it.system == context.getString(R.string.sync_strategy_related_entity_location_system) - && locationIds.contains(it.code) + searchResultsCount += + fhirEngine + .search(baseResourceSearch) + .asSequence() + .map { it.resource } + .filter { resource -> + when (resource.resourceType) { + ResourceType.Location -> locationIds.contains(resource.logicalId) + else -> + resource.meta.tag.any { + it.system == + context.getString(R.string.sync_strategy_related_entity_location_system) && + locationIds.contains(it.code) + } } } - }.count().toLong() + .count() + .toLong() count += COUNT pageNumber++ } @@ -164,7 +170,7 @@ constructor( onFailure = { Timber.e( it, - "Error counting register data for register id: ${registerConfiguration.id}" + "Error counting register data for register id: ${registerConfiguration.id}", ) }, ) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt index 0f82f47883..efa101b299 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt @@ -189,7 +189,9 @@ constructor( accountManager.peekAuthToken(account, AUTH_TOKEN_TYPE), ) Result.success(true) - } else Result.success(false) + } else { + Result.success(false) + } } catch (httpException: HttpException) { Result.failure(httpException) } catch (unknownHostException: UnknownHostException) { diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/di/NetworkModule.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/di/NetworkModule.kt index e2c346fc93..1ce80b7bf7 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/di/NetworkModule.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/di/NetworkModule.kt @@ -66,7 +66,9 @@ class NetworkModule { level = if (BuildConfig.DEBUG) { HttpLoggingInterceptor.Level.BODY - } else HttpLoggingInterceptor.Level.BASIC + } else { + HttpLoggingInterceptor.Level.BASIC + } redactHeader(AUTHORIZATION) redactHeader(COOKIE) }, @@ -141,7 +143,9 @@ class NetworkModule { level = if (BuildConfig.DEBUG) { HttpLoggingInterceptor.Level.BODY - } else HttpLoggingInterceptor.Level.BASIC + } else { + HttpLoggingInterceptor.Level.BASIC + } redactHeader(AUTHORIZATION) redactHeader(COOKIE) }, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt index 28aaf581ed..bcdb51f226 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt @@ -140,7 +140,9 @@ class HtmlPopulator( questionnaireResponseItemMap.getOrDefault(linkId, listOf()).joinToString { answer -> if (dateFormat == null) { answer.value.valueToString() - } else answer.value.valueToString(dateFormat) + } else { + answer.value.valueToString(dateFormat) + } } html.replace(i, matcher.end() + i, answer) } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/ConfigRulesExecutor.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/ConfigRulesExecutor.kt index 7c1821d6b4..c03ff0a495 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/ConfigRulesExecutor.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/ConfigRulesExecutor.kt @@ -52,7 +52,9 @@ class ConfigRulesExecutor @Inject constructor(val fhirPathDataExtractor: FhirPat if (BuildConfig.DEBUG) { val timeToFireRules = measureTimeMillis { rulesEngine.fire(rules, facts) } Timber.d("Rule executed in $timeToFireRules millisecond(s)") - } else rulesEngine.fire(rules, facts) + } else { + rulesEngine.fire(rules, facts) + } return facts.get(DATA) as Map } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/ResourceDataRulesExecutor.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/ResourceDataRulesExecutor.kt index fce7444a95..6124b286ad 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/ResourceDataRulesExecutor.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/ResourceDataRulesExecutor.kt @@ -186,7 +186,9 @@ class ResourceDataRulesExecutor @Inject constructor(val rulesFactory: RulesFacto resources = newListRelatedResources, conditionalFhirPathExpression = listResource.conditionalFhirPathExpression, ) - } else newListRelatedResources ?: listOf() + } else { + newListRelatedResources ?: listOf() + } val sortConfig = listResource.sortConfig diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/RulesFactory.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/RulesFactory.kt index 9e563579fe..77a8e41935 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/RulesFactory.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/RulesFactory.kt @@ -140,7 +140,9 @@ constructor( if (BuildConfig.DEBUG) { val timeToFireRules = measureTimeMillis { rulesEngine.fire(rules, facts) } Timber.d("Rule executed in $timeToFireRules millisecond(s)") - } else rulesEngine.fire(rules, facts) + } else { + rulesEngine.fire(rules, facts) + } return facts.get(DATA) as Map } @@ -192,13 +194,14 @@ constructor( return if (referenceFhirPathExpression.isNullOrEmpty()) { value - } else + } else { value.filter { resource.logicalId == fhirPathDataExtractor .extractValue(it, referenceFhirPathExpression) .extractLogicalIdUuid() } + } } /** @@ -686,7 +689,9 @@ constructor( } if (createLocalChangeEntitiesAfterPurge) { defaultRepository.addOrUpdate(resource = updatedResource as Resource) - } else defaultRepository.createRemote(resource = arrayOf(updatedResource as Resource)) + } else { + defaultRepository.createRemote(resource = arrayOf(updatedResource as Resource)) + } } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt index 6fee7dfe88..9f907d8e9e 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/FhirCarePlanGenerator.kt @@ -396,7 +396,9 @@ constructor( end = if (durationExpression.isNotBlank() && offsetDate.hasValue()) { evaluateToDate(offsetDate, "\$this + $durationExpression")?.value - } else carePlan.period.end + } else { + carePlan.period.end + } } .also { taskPeriods.add(it) } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/base/AlertDialogue.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/base/AlertDialogue.kt index 677cf7b786..b329d1a556 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/base/AlertDialogue.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/base/AlertDialogue.kt @@ -81,7 +81,9 @@ object AlertDialogue { dialog.findViewById(R.id.pr_circular)?.apply { if (alertIntent == AlertIntent.PROGRESS) { this.show() - } else this.hide() + } else { + this.hide() + } } dialog.findViewById(R.id.tv_alert_message)?.apply { this.text = message } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/Pin.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/Pin.kt index 5487b9d92e..7b6eb5619c 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/Pin.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/Pin.kt @@ -114,7 +114,9 @@ fun PinInput( enteredPin = if (it.length < enteredPin.size) { enteredPin.safeRemoveLast() - } else enteredPin.safePlus(it.last()) + } else { + enteredPin.safePlus(it.last()) + } nextCellIndex = enteredPin.size onPinSet(enteredPin) onShowPinError(false) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/RegisterFooter.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/RegisterFooter.kt index 93a64f7537..fdddeae1ea 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/RegisterFooter.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/components/register/RegisterFooter.kt @@ -43,7 +43,6 @@ const val SEARCH_FOOTER_PREVIOUS_BUTTON_TAG = "searchFooterPreviousButtonTag" const val SEARCH_FOOTER_NEXT_BUTTON_TAG = "searchFooterNextButtonTag" const val SEARCH_FOOTER_PAGINATION_TAG = "searchFooterPaginationTag" - @Composable fun RegisterFooter( resultCount: Int, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt index 212ffcceaa..59bb1305d8 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/multiselect/MultiSelectView.kt @@ -87,7 +87,9 @@ fun MultiSelectCheckbox( imageVector = if (collapsedState.value) { Icons.Default.ArrowDropDown - } else Icons.AutoMirrored.Filled.ArrowRight, + } else { + Icons.AutoMirrored.Filled.ArrowRight + }, contentDescription = null, tint = Color.Gray, modifier = Modifier.clickable { collapsedState.value = !collapsedState.value }, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/AndroidExtensions.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/AndroidExtensions.kt index 806075ad5b..7d873a8d40 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/AndroidExtensions.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/AndroidExtensions.kt @@ -31,11 +31,14 @@ import android.os.LocaleList import android.os.Parcelable import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.compose.ui.graphics.Color as ComposeColor import androidx.compose.ui.state.ToggleableState import androidx.core.os.bundleOf import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding +import java.io.Serializable +import java.util.Locale import kotlinx.coroutines.flow.firstOrNull import org.smartregister.fhircore.engine.datastore.syncLocationIdsProtoStore import org.smartregister.fhircore.engine.ui.theme.DangerColor @@ -46,9 +49,6 @@ import org.smartregister.fhircore.engine.ui.theme.SuccessColor import org.smartregister.fhircore.engine.ui.theme.WarningColor import org.smartregister.fhircore.engine.util.annotation.ExcludeFromJacocoGeneratedReport import timber.log.Timber -import java.io.Serializable -import java.util.Locale -import androidx.compose.ui.graphics.Color as ComposeColor const val ERROR_COLOR = "errorColor" const val PRIMARY_COLOR = "primaryColor" diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/MeasureExtensions.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/MeasureExtensions.kt index dd733712c4..c889b3566e 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/MeasureExtensions.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/MeasureExtensions.kt @@ -92,7 +92,7 @@ fun MeasureReport.StratifierGroupComponent.findPercentage( ): String { return if (denominator == 0) { "0" - } else + } else { findPopulation(MeasurePopulationType.NUMERATOR) ?.count ?.toBigDecimal() @@ -103,6 +103,7 @@ fun MeasureReport.StratifierGroupComponent.findPercentage( reportConfiguration?.roundingStrategy?.value ?: DEFAULT_ROUNDING_STRATEGY.value, ) .toString() + } } val MeasureReport.StratifierGroupComponent.displayText diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireExtension.kt index d036dfdde6..cbb925e223 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireExtension.kt @@ -183,7 +183,9 @@ fun List.prePopulateInitialValues( (it.value is Coding) && if (actionParam.value.contains(",")) { actionParam.value.split(",").contains((it.value as Coding).code) - } else actionParam.value == (it.value as Coding).code + } else { + actionParam.value == (it.value as Coding).code + } } .forEach { it.initialSelected = true } } else { diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ReferenceExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ReferenceExtension.kt index d833d7b9b0..c31a255128 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ReferenceExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ReferenceExtension.kt @@ -25,10 +25,11 @@ fun Reference.extractId(): String = fun Reference.extractType(): ResourceType? = if (this.reference.isNullOrEmpty()) { null - } else + } else { this.reference.substringBefore("/" + this.extractId()).substringAfterLast("/").let { ResourceType.fromCode(it) } + } fun String.asReference(resourceType: ResourceType): Reference { val resourceId = this diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt index 2f87eac395..bb6e5b55b0 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt @@ -24,6 +24,12 @@ import com.google.android.fhir.datacapture.extensions.createQuestionnaireRespons import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.get import com.google.android.fhir.search.search +import java.time.Duration +import java.time.temporal.ChronoUnit +import java.util.Date +import java.util.Locale +import java.util.UUID +import kotlin.math.abs import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.model.Base import org.hl7.fhir.r4.model.BaseDateTimeType @@ -67,12 +73,6 @@ import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import timber.log.Timber -import java.time.Duration -import java.time.temporal.ChronoUnit -import java.util.Date -import java.util.Locale -import java.util.UUID -import kotlin.math.abs const val REFERENCE = "reference" const val PARTOF = "part-of" diff --git a/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModel.kt b/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModel.kt index dc2073653f..e0b4e4bf3f 100644 --- a/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModel.kt +++ b/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModel.kt @@ -19,10 +19,10 @@ package org.smartregister.fhircore.geowidget.screens import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.geowidget.model.GeoJsonFeature import org.smartregister.fhircore.geowidget.model.ServicePointType -import javax.inject.Inject @HiltViewModel class GeoWidgetViewModel @Inject constructor(val dispatcherProvider: DispatcherProvider) : diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/DataMigration.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/DataMigration.kt index 8807b5d3e6..a565ea5d29 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/DataMigration.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/DataMigration.kt @@ -211,7 +211,9 @@ constructor( } if (migrationConfig.createLocalChangeEntitiesAfterPurge) { defaultRepository.addOrUpdate(resource = updatedResource as Resource) - } else defaultRepository.createRemote(resource = *arrayOf(updatedResource as Resource)) + } else { + defaultRepository.createRemote(resource = *arrayOf(updatedResource as Resource)) + } } } Timber.i("Data migration completed successfully for version: ${migrationConfig.version}") diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/register/RegisterPagingSource.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/register/RegisterPagingSource.kt index 42e3ef14b7..f489eba575 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/register/RegisterPagingSource.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/register/RegisterPagingSource.kt @@ -66,8 +66,11 @@ class RegisterPagingSource( val prevKey = if (_registerPagingSourceState.loadAll && currentPage > 0) currentPage - 1 else null val nextKey = - if (_registerPagingSourceState.loadAll && registerData.isNotEmpty()) currentPage + 1 - else null + if (_registerPagingSourceState.loadAll && registerData.isNotEmpty()) { + currentPage + 1 + } else { + null + } val data = registerData.map { repositoryResourceData -> diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt index 03e1cd12fb..8ac0f9d169 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt @@ -172,13 +172,14 @@ constructor( entry.key, parentIt.map { it.focus.extractId() }, ) - } else + } else { fhirResourceDataSource.post( requestBody = generateRequestBundle(entry.key, parentIt.map { it.focus.extractId() }) .encodeResourceToString() .toRequestBody(NetworkModule.JSON_MEDIA_TYPE), ) + } resultBundle.entry.forEach { bundleEntryComponent -> if (bundleEntryComponent.resource != null) { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/login/LoginActivity.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/login/LoginActivity.kt index 966faf9bca..f873e96df6 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/login/LoginActivity.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/login/LoginActivity.kt @@ -90,7 +90,9 @@ open class LoginActivity : BaseMultiLanguageActivity() { downloadNowWorkflowConfigs() if (isPinEnabled && !hasActivePin) { navigateToPinLogin(launchSetup = true) - } else loginActivity.navigateToHome() + } else { + loginActivity.navigateToHome() + } } } launchDialPad.observe(loginActivity) { if (!it.isNullOrBlank()) launchDialPad(it) } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt index f8e9363b3b..81c1f8371b 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt @@ -199,7 +199,9 @@ open class AppMainActivity : BaseMultiLanguageActivity(), QuestionnaireHandler, ), ), ) - } else Timber.e("QuestionnaireConfig & QuestionnaireResponse are both null") + } else { + Timber.e("QuestionnaireConfig & QuestionnaireResponse are both null") + } } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt index 04cae4f44c..0a7fce88db 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt @@ -34,6 +34,13 @@ import com.google.android.fhir.FhirEngine import com.google.android.fhir.sync.CurrentSyncJobStatus import com.google.android.fhir.sync.SyncJobStatus import dagger.hilt.android.lifecycle.HiltViewModel +import java.text.SimpleDateFormat +import java.time.OffsetDateTime +import java.util.Date +import java.util.Locale +import java.util.TimeZone +import javax.inject.Inject +import kotlin.time.Duration import kotlinx.coroutines.async import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch @@ -81,13 +88,6 @@ import org.smartregister.fhircore.quest.ui.shared.models.QuestionnaireSubmission import org.smartregister.fhircore.quest.util.extensions.handleClickEvent import org.smartregister.fhircore.quest.util.extensions.resourceReferenceToBitMap import org.smartregister.fhircore.quest.util.extensions.schedulePeriodically -import java.text.SimpleDateFormat -import java.time.OffsetDateTime -import java.util.Date -import java.util.Locale -import java.util.TimeZone -import javax.inject.Inject -import kotlin.time.Duration @HiltViewModel class AppMainViewModel @@ -134,14 +134,15 @@ constructor( } fun retrieveIconsAsBitmap() { - viewModelScope.launch (dispatcherProvider.io()){ + viewModelScope.launch(dispatcherProvider.io()) { navigationConfiguration.clientRegisters .asSequence() .filter { it.menuIconConfig != null && - it.menuIconConfig?.type == ICON_TYPE_REMOTE && - !it.menuIconConfig?.reference.isNullOrBlank() - }.mapNotNull { it.menuIconConfig!!.reference } + it.menuIconConfig?.type == ICON_TYPE_REMOTE && + !it.menuIconConfig?.reference.isNullOrBlank() + } + .mapNotNull { it.menuIconConfig!!.reference } .resourceReferenceToBitMap( fhirEngine = fhirEngine, decodedImageMap = configurationRegistry.decodedImageMap, @@ -291,11 +292,15 @@ constructor( NavigationArg.SCREEN_TITLE to if (startDestinationConfig.screenTitle.isNullOrEmpty()) { topMenuConfig.display - } else startDestinationConfig.screenTitle, + } else { + startDestinationConfig.screenTitle + }, NavigationArg.REGISTER_ID to if (startDestinationConfig.id.isNullOrEmpty()) { clickAction?.id ?: topMenuConfig.id - } else startDestinationConfig.id, + } else { + startDestinationConfig.id + }, ) } LauncherType.MAP -> bundleOf(NavigationArg.GEO_WIDGET_ID to startDestinationConfig.id) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/components/AppDrawer.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/components/AppDrawer.kt index 0ae4299164..2e2d6b3362 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/components/AppDrawer.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/components/AppDrawer.kt @@ -321,7 +321,9 @@ private fun DefaultSyncStatus( Modifier.background( if (allDataSynced) { SideMenuBottomItemDarkColor - } else WarningColor.copy(alpha = TRANSPARENCY), + } else { + WarningColor.copy(alpha = TRANSPARENCY) + }, ) .padding(vertical = 16.dp), ) { @@ -332,7 +334,9 @@ private fun DefaultSyncStatus( stringResource( if (allDataSynced) { org.smartregister.fhircore.engine.R.string.manual_sync - } else org.smartregister.fhircore.engine.R.string.sync, + } else { + org.smartregister.fhircore.engine.R.string.sync + }, ), subTitle = if (allDataSynced) { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt index 64799c70b9..885990d23f 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/multiselect/MultiSelectViewModel.kt @@ -26,6 +26,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.android.fhir.datacapture.extensions.logicalId import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import org.smartregister.fhircore.engine.data.local.DefaultRepository @@ -36,7 +37,6 @@ import org.smartregister.fhircore.engine.ui.multiselect.TreeBuilder import org.smartregister.fhircore.engine.ui.multiselect.TreeNode import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor -import javax.inject.Inject @HiltViewModel class MultiSelectViewModel diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileScreen.kt index 1037a76430..04a946015a 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileScreen.kt @@ -163,7 +163,9 @@ fun ProfileScreen( bottom = if (!fabActions.isNullOrEmpty() && fabActions.first().visible) { PADDING_BOTTOM_WITH_FAB.dp - } else PADDING_BOTTOM_WITHOUT_FAB.dp, + } else { + PADDING_BOTTOM_WITHOUT_FAB.dp + }, ), ) { item(key = profileUiState.resourceData?.baseResourceId) { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModel.kt index 219cd3b6e5..1673f814d2 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModel.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.viewModelScope import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.db.ResourceNotFoundException import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -59,7 +60,6 @@ import org.smartregister.fhircore.quest.util.extensions.handleClickEvent import org.smartregister.fhircore.quest.util.extensions.resourceReferenceToBitMap import org.smartregister.fhircore.quest.util.extensions.toParamDataMap import timber.log.Timber -import javax.inject.Inject @HiltViewModel class ProfileViewModel @@ -149,7 +149,7 @@ constructor( withContext(dispatcherProvider.io()) { profileConfigs.views.decodeImageResourcesToBitmap( fhirEngine = registerRepository.fhirEngine, - decodedImageMap = configurationRegistry.decodedImageMap + decodedImageMap = configurationRegistry.decodedImageMap, ) } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/components/ChangeManagingEntityView.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/components/ChangeManagingEntityView.kt index d7b6c97d24..229dc75593 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/components/ChangeManagingEntityView.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/profile/components/ChangeManagingEntityView.kt @@ -154,7 +154,9 @@ private fun ChangeManagingEntityBottomBar( id = if (isEnabled) { org.smartregister.fhircore.engine.R.color.colorPrimary - } else org.smartregister.fhircore.engine.R.color.white, + } else { + org.smartregister.fhircore.engine.R.color.white + }, ), ), ) { @@ -165,7 +167,9 @@ private fun ChangeManagingEntityBottomBar( id = if (isEnabled) { org.smartregister.fhircore.engine.R.color.white - } else org.smartregister.fhircore.engine.R.color.colorPrimary, + } else { + org.smartregister.fhircore.engine.R.color.colorPrimary + }, ), text = stringResource(id = org.smartregister.fhircore.engine.R.string.str_save).uppercase(), ) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 50f1520793..971003c944 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -36,6 +36,10 @@ import com.google.android.fhir.search.filter.TokenParamFilterCriterion import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.lifecycle.HiltViewModel +import java.util.Date +import java.util.UUID +import javax.inject.Inject +import javax.inject.Provider import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -95,10 +99,6 @@ import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import org.smartregister.fhircore.engine.util.helper.TransformSupportServices import org.smartregister.fhircore.quest.R import timber.log.Timber -import java.util.Date -import java.util.UUID -import javax.inject.Inject -import javax.inject.Provider @HiltViewModel class QuestionnaireViewModel diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt index 601d529406..03d1bb1acf 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterFragment.kt @@ -46,6 +46,7 @@ import com.google.android.fhir.sync.CurrentSyncJobStatus import com.google.android.fhir.sync.SyncJobStatus import com.google.android.fhir.sync.SyncOperation import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -68,7 +69,6 @@ import org.smartregister.fhircore.quest.ui.shared.viewmodels.SearchViewModel import org.smartregister.fhircore.quest.util.extensions.handleClickEvent import org.smartregister.fhircore.quest.util.extensions.hookSnackBar import org.smartregister.fhircore.quest.util.extensions.rememberLifecycleEvent -import javax.inject.Inject @ExperimentalMaterialApi @AndroidEntryPoint diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterScreen.kt index 5facb82ae5..54e4bf4e5d 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterScreen.kt @@ -171,7 +171,9 @@ fun RegisterScreen( id = if (appDrawerUIState.isSyncUpload == true) { R.string.syncing_up - } else R.string.syncing_down, + } else { + R.string.syncing_down + }, ), showPercentageProgress = true, ) @@ -191,8 +193,9 @@ fun RegisterScreen( onEvent = onEvent, registerUiState = registerUiState, currentPage = currentPage, - showPagination = !registerUiState.registerConfiguration.infiniteScroll && - searchQuery.value.isBlank(), + showPagination = + !registerUiState.registerConfiguration.infiniteScroll && + searchQuery.value.isBlank(), onSearchByQrSingleResultAction = { resourceData -> if ( !searchQuery.value.isBlank() && searchQuery.value.mode == SearchMode.QrCodeScan diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt index 66834c58c0..1aa74b6290 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt @@ -29,6 +29,8 @@ import androidx.paging.cachedIn import androidx.paging.filter import com.google.android.fhir.sync.CurrentSyncJobStatus import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlin.math.ceil import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -62,8 +64,6 @@ import org.smartregister.fhircore.quest.data.register.RegisterPagingSource import org.smartregister.fhircore.quest.data.register.model.RegisterPagingSourceState import org.smartregister.fhircore.quest.util.extensions.toParamDataMap import timber.log.Timber -import javax.inject.Inject -import kotlin.math.ceil @HiltViewModel class RegisterViewModel @@ -154,8 +154,10 @@ constructor( return registerConfiguration } - private fun retrieveCompleteRegisterData(registerId: String, forceRefresh: Boolean): - Flow> { + private fun retrieveCompleteRegisterData( + registerId: String, + forceRefresh: Boolean, + ): Flow> { if (completeRegisterData == null || forceRefresh) { completeRegisterData = getPager(registerId, true).flow.cachedIn(viewModelScope) } @@ -171,24 +173,23 @@ constructor( val regConfig = retrieveRegisterConfiguration(registerId) if (regConfig.infiniteScroll) { registerData.value = retrieveCompleteRegisterData(registerId, false) - } else paginateRegisterData(registerId) + } else { + paginateRegisterData(registerId) + } } else { filterRegisterData(event.searchQuery.query) } } - is RegisterEvent.MoveToNextPage -> { currentPage.value = currentPage.value.plus(1) paginateRegisterData(registerId) } - is RegisterEvent.MoveToPreviousPage -> { currentPage.value.let { if (it > 0) currentPage.value = it.minus(1) } paginateRegisterData(registerId) } - RegisterEvent.ResetFilterRecordsCount -> _filteredRecordsCount.longValue = -1 - } + } } fun filterRegisterData(searchText: String) { @@ -456,7 +457,8 @@ constructor( viewModelScope.launch { val currentRegisterConfiguration = retrieveRegisterConfiguration(registerId, paramsMap) if (currentRegisterConfiguration.infiniteScroll) { - registerData.value = retrieveCompleteRegisterData(currentRegisterConfiguration.id, clearCache) + registerData.value = + retrieveCompleteRegisterData(currentRegisterConfiguration.id, clearCache) } else { _totalRecordsCount.longValue = registerRepository.countRegisterData(registerId = registerId, paramsMap = paramsMap) @@ -491,11 +493,11 @@ constructor( filteredRecordsCount = _filteredRecordsCount.longValue, pagesCount = ceil( - ( - if (registerFilterState.value.fhirResourceConfig != null) { - _filteredRecordsCount.longValue - } else _totalRecordsCount.longValue - ) + (if (registerFilterState.value.fhirResourceConfig != null) { + _filteredRecordsCount.longValue + } else { + _totalRecordsCount.longValue + }) .toDouble() .div(currentRegisterConfiguration.pageSize.toLong()), ) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/components/RegisterCardList.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/components/RegisterCardList.kt index a964f7a85a..194038b148 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/components/RegisterCardList.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/components/RegisterCardList.kt @@ -117,12 +117,17 @@ fun RegisterCardList( // Register pagination item { val fabActions = registerUiState.registerConfiguration?.fabActions - Box(modifier = Modifier.padding( - bottom = - if (!fabActions.isNullOrEmpty() && fabActions.first().visible) { - PADDING_BOTTOM_WITH_FAB.dp - } else PADDING_BOTTOM_WITHOUT_FAB.dp, - )) { + Box( + modifier = + Modifier.padding( + bottom = + if (!fabActions.isNullOrEmpty() && fabActions.first().visible) { + PADDING_BOTTOM_WITH_FAB.dp + } else { + PADDING_BOTTOM_WITHOUT_FAB.dp + }, + ), + ) { if (pagingItems.itemCount > 0 && showPagination) { RegisterFooter( resultCount = pagingItems.itemCount, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeItemViewHolderFactory.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeItemViewHolderFactory.kt index d7a4bcf438..468f58c079 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeItemViewHolderFactory.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeItemViewHolderFactory.kt @@ -92,9 +92,10 @@ internal class EditTextQrCodeItemViewHolderFactory( editable.toString().let { if (it.isBlank()) { null - } else + } else { QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent() .setValue(StringType(it)) + } } qrCodeAnswerChangeListener.onQrCodeChanged( diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeViewHolderFactory.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeViewHolderFactory.kt index 6e1c248eab..3bac623fb0 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeViewHolderFactory.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeViewHolderFactory.kt @@ -60,7 +60,9 @@ object EditTextQrCodeViewHolderFactory : prevAnswerEmpty && !newAnswerEmpty -> { if (canHaveMultipleAnswers) { questionnaireViewItem.addAnswer(newAnswer!!) - } else questionnaireViewItem.setAnswer(newAnswer!!) + } else { + questionnaireViewItem.setAnswer(newAnswer!!) + } } !prevAnswerEmpty && newAnswerEmpty -> { questionnaireViewItem.removeAnswer(previousAnswer!!) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/ActionableButton.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/ActionableButton.kt index 77a7b46631..103b2795cd 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/ActionableButton.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/ActionableButton.kt @@ -192,7 +192,9 @@ fun ActionableButton( } else { if (colorOpacity == 0.0f) { DefaultColor.copy(alpha = 0.9f) - } else statusColor.copy(alpha = colorOpacity) + } else { + statusColor.copy(alpha = colorOpacity) + } }, textAlign = TextAlign.Start, overflow = TextOverflow.Ellipsis, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/SyncStatusView.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/SyncStatusView.kt index 2783138e71..423323e660 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/SyncStatusView.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/SyncStatusView.kt @@ -106,7 +106,9 @@ fun SyncBottomBar( val bottomRadius = if (!hideSyncCompleteStatus.value || currentSyncJobStatus is CurrentSyncJobStatus.Running) { 32.dp - } else 0.dp + } else { + 0.dp + } val height = when { syncNotificationBarExpanded -> @@ -147,7 +149,9 @@ fun SyncBottomBar( imageVector = if (syncNotificationBarExpanded) { Icons.Default.KeyboardArrowDown - } else Icons.Default.KeyboardArrowUp, + } else { + Icons.Default.KeyboardArrowUp + }, contentDescription = null, tint = when (currentSyncJobStatus) { @@ -237,7 +241,9 @@ fun SyncStatusView( imageVector = if (currentSyncJobStatus is CurrentSyncJobStatus.Succeeded) { Icons.Default.CheckCircle - } else Icons.Default.Error, + } else { + Icons.Default.Error + }, contentDescription = null, tint = when (currentSyncJobStatus) { @@ -308,7 +314,9 @@ fun SyncStatusView( stringResource( if (currentSyncJobStatus is CurrentSyncJobStatus.Failed) { org.smartregister.fhircore.engine.R.string.retry - } else org.smartregister.fhircore.engine.R.string.cancel, + } else { + org.smartregister.fhircore.engine.R.string.cancel + }, ), modifier = Modifier.padding(start = 16.dp).clickable { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/ViewRenderer.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/ViewRenderer.kt index 8855bb2021..276d67c769 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/ViewRenderer.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/components/ViewRenderer.kt @@ -58,7 +58,9 @@ fun ViewRenderer( val interpolatedProperties = if (areViewPropertiesInterpolated) { properties - } else properties.interpolate(resourceData.computedValuesMap) + } else { + properties.interpolate(resourceData.computedValuesMap) + } GenerateView( modifier = generateModifier(properties), properties = interpolatedProperties, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt index 0fc6c394bc..a3b7a6979d 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt @@ -30,6 +30,7 @@ import androidx.core.os.bundleOf import androidx.navigation.NavController import androidx.navigation.NavOptions import com.google.android.fhir.FhirEngine +import kotlin.collections.set import org.hl7.fhir.r4.model.Binary import org.smartregister.fhircore.engine.configuration.navigation.ICON_TYPE_REMOTE import org.smartregister.fhircore.engine.configuration.navigation.NavigationMenuConfig @@ -62,7 +63,6 @@ import org.smartregister.fhircore.quest.navigation.NavigationArg import org.smartregister.fhircore.quest.ui.pdf.PdfLauncherFragment import org.smartregister.fhircore.quest.ui.shared.QuestionnaireHandler import org.smartregister.p2p.utils.startP2PScreen -import kotlin.collections.set const val PRACTITIONER_ID = "practitionerId" @@ -261,12 +261,12 @@ suspend fun Sequence.resourceReferenceToBitMap( fhirEngine: FhirEngine, decodedImageMap: SnapshotStateMap, ) { - forEach { - val resourceId = it.extractLogicalIdUuid() - fhirEngine.loadResource(resourceId)?.let { binary -> - decodedImageMap[resourceId] = binary.data.decodeToBitmap() - } + forEach { + val resourceId = it.extractLogicalIdUuid() + fhirEngine.loadResource(resourceId)?.let { binary -> + decodedImageMap[resourceId] = binary.data.decodeToBitmap() } + } } suspend fun List.decodeImageResourcesToBitmap( @@ -276,13 +276,15 @@ suspend fun List.decodeImageResourcesToBitmap( val queue = ArrayDeque(this) while (queue.isNotEmpty()) { val viewProperty = queue.removeFirst() - when(viewProperty.viewType) { + when (viewProperty.viewType) { ViewType.IMAGE -> { val imageProperties = (viewProperty as ImageProperties) if (imageProperties.imageConfig != null) { val imageConfig = imageProperties.imageConfig - if (ICON_TYPE_REMOTE.equals(imageConfig?.type, ignoreCase = true) && - !imageConfig?.reference.isNullOrBlank()) { + if ( + ICON_TYPE_REMOTE.equals(imageConfig?.type, ignoreCase = true) && + !imageConfig?.reference.isNullOrBlank() + ) { val resourceId = imageConfig!!.reference!! fhirEngine.loadResource(resourceId)?.let { binary: Binary -> decodedImageMap[resourceId] = binary.data.decodeToBitmap() @@ -291,13 +293,14 @@ suspend fun List.decodeImageResourcesToBitmap( } } ViewType.COLUMN -> (viewProperty as ColumnProperties).children.forEach(queue::addLast) - ViewType.ROW -> (viewProperty as RowProperties).children.forEach(queue::addLast) - ViewType.SERVICE_CARD -> (viewProperty as ServiceCardProperties).details.forEach(queue::addLast) + ViewType.ROW -> (viewProperty as RowProperties).children.forEach(queue::addLast) + ViewType.SERVICE_CARD -> + (viewProperty as ServiceCardProperties).details.forEach(queue::addLast) ViewType.CARD -> (viewProperty as CardViewProperties).content.forEach(queue::addLast) ViewType.LIST -> (viewProperty as ListProperties).registerCard.views.forEach(queue::addLast) ViewType.STACK -> (viewProperty as StackViewProperties).children.forEach(queue::addLast) else -> { - /**Ignore other views that cannot display images**/ + /** Ignore other views that cannot display images* */ } } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index d9724ab580..14a356e110 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -220,7 +220,9 @@ class CqlContentTest : RobolectricTest() { it.name to if (it.hasResource()) { it.resource.encodeResourceToString() - } else it.valueToString() + } else { + it.valueToString() + } } val expectedResource = resource.parseSampleResourceFromFile().convertToString(true) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt index 55f632705b..a8e7bf5ee7 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt @@ -28,6 +28,7 @@ import io.mockk.mockk import io.mockk.runs import io.mockk.spyk import io.mockk.verify +import javax.inject.Inject import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.DateType @@ -59,7 +60,6 @@ import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.quest.app.fakes.Faker import org.smartregister.fhircore.quest.robolectric.RobolectricTest import org.smartregister.fhircore.quest.ui.shared.models.SearchQuery -import javax.inject.Inject @HiltAndroidTest class RegisterViewModelTest : RobolectricTest() { diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt index db2020c2ba..64612cd226 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt @@ -37,6 +37,7 @@ import io.mockk.every import io.mockk.mockk import io.mockk.slot import io.mockk.verify +import javax.inject.Inject import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Binary @@ -79,7 +80,6 @@ import org.smartregister.fhircore.quest.navigation.MainNavigationScreen import org.smartregister.fhircore.quest.navigation.NavigationArg import org.smartregister.fhircore.quest.robolectric.RobolectricTest import org.smartregister.fhircore.quest.ui.shared.QuestionnaireHandler -import javax.inject.Inject @HiltAndroidTest class ConfigExtensionsKtTest : RobolectricTest() { @@ -673,7 +673,8 @@ class ConfigExtensionsKtTest : RobolectricTest() { fun decodeBinaryResourcesToBitmapOnNavigationMenuClientRegistersDoneCorrectly(): Unit = runBlocking { defaultRepository.create(addResourceTags = true, binaryImage) - val navigationMenuConfigs = sequenceOf(navigationMenuConfig).mapNotNull { it.menuIconConfig?.reference } + val navigationMenuConfigs = + sequenceOf(navigationMenuConfig).mapNotNull { it.menuIconConfig?.reference } val decodedImageMap = mutableStateMapOf() runBlocking { navigationMenuConfigs.resourceReferenceToBitMap( @@ -691,8 +692,10 @@ class ConfigExtensionsKtTest : RobolectricTest() { val navigationMenuConfigs = sequenceOf(overflowMenuItemConfig).mapNotNull { it.icon?.reference } val decodedImageMap = mutableStateMapOf() runBlocking { - navigationMenuConfigs.resourceReferenceToBitMap( fhirEngine = fhirEngine, - decodedImageMap = decodedImageMap,) + navigationMenuConfigs.resourceReferenceToBitMap( + fhirEngine = fhirEngine, + decodedImageMap = decodedImageMap, + ) } Assert.assertTrue(decodedImageMap.isNotEmpty()) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) @@ -733,7 +736,8 @@ class ConfigExtensionsKtTest : RobolectricTest() { val listViewProperties = cardViewProperties.content[0] as ListProperties val decodedImageMap = mutableStateMapOf() defaultRepository.create(addResourceTags = true, binaryImage) - listOf(listViewProperties.registerCard.views[0]).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + listOf(listViewProperties.registerCard.views[0]) + .decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } From 513c8051f6e0717b0b95d7882586327446d2adb5 Mon Sep 17 00:00:00 2001 From: Lentumunai Mark <90028422+Lentumunai-Mark@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:00:38 +0300 Subject: [PATCH 056/110] Update tests for displaying images (#3506) * Refactor load images tests for different views. Signed-off-by: Lentumunai-Mark * Remove unutilized imports. Signed-off-by: Lentumunai-Mark * Run spotlessApply Signed-off-by: Elly Kitoto Signed-off-by: Lentumunai-Mark * Refactor load images tests for different views. Signed-off-by: Lentumunai-Mark * Resolve conflicts. Signed-off-by: Lentumunai-Mark --------- Signed-off-by: Lentumunai-Mark Signed-off-by: Elly Kitoto --- .../engine/data/local/DefaultRepository.kt | 6 +- .../util/extensions/ConfigExtensionsKtTest.kt | 86 ++++++++----------- 2 files changed, 38 insertions(+), 54 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index c6e1c3f66f..ad62b3cfee 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -43,6 +43,9 @@ import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.Option import com.jayway.jsonpath.PathNotFoundException import dagger.hilt.android.qualifiers.ApplicationContext +import java.util.UUID +import javax.inject.Inject +import kotlin.math.min import kotlinx.coroutines.withContext import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement @@ -95,9 +98,6 @@ import org.smartregister.fhircore.engine.util.extension.updateLastUpdated import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import org.smartregister.fhircore.engine.util.pmap import timber.log.Timber -import java.util.UUID -import javax.inject.Inject -import kotlin.math.min open class DefaultRepository @Inject diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt index 64612cd226..3130b55387 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt @@ -32,22 +32,21 @@ import com.google.android.fhir.FhirEngine import com.google.android.fhir.datacapture.extensions.logicalId import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest -import io.mockk.coEvery import io.mockk.every import io.mockk.mockk import io.mockk.slot import io.mockk.verify import javax.inject.Inject +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest -import org.hl7.fhir.r4.model.Binary +import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.ContactPoint import org.hl7.fhir.r4.model.ResourceType import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test -import org.mockito.ArgumentMatchers.anyString import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig import org.smartregister.fhircore.engine.configuration.navigation.ICON_TYPE_REMOTE @@ -73,6 +72,7 @@ import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.domain.model.ResourceData import org.smartregister.fhircore.engine.domain.model.ToolBarHomeNavigation import org.smartregister.fhircore.engine.domain.model.ViewType +import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.showToast import org.smartregister.fhircore.quest.R import org.smartregister.fhircore.quest.app.fakes.Faker @@ -95,6 +95,8 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Inject lateinit var fhirEngine: FhirEngine + @Inject lateinit var dispatcherProvider: DispatcherProvider + private val navController = mockk(relaxUnitFun = true, relaxed = true) private val context = mockk(relaxUnitFun = true, relaxed = true) private val navigationMenuConfig by lazy { @@ -672,11 +674,11 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Test fun decodeBinaryResourcesToBitmapOnNavigationMenuClientRegistersDoneCorrectly(): Unit = runBlocking { - defaultRepository.create(addResourceTags = true, binaryImage) val navigationMenuConfigs = sequenceOf(navigationMenuConfig).mapNotNull { it.menuIconConfig?.reference } val decodedImageMap = mutableStateMapOf() - runBlocking { + withContext(dispatcherProvider.io()) { + defaultRepository.create(addResourceTags = true, binaryImage) navigationMenuConfigs.resourceReferenceToBitMap( fhirEngine = fhirEngine, decodedImageMap = decodedImageMap, @@ -688,10 +690,10 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Test fun decodeBinaryResourcesToBitmapOnOverflowMenuConfigDoneCorrectly(): Unit = runTest { - defaultRepository.create(addResourceTags = true, binaryImage) val navigationMenuConfigs = sequenceOf(overflowMenuItemConfig).mapNotNull { it.icon?.reference } val decodedImageMap = mutableStateMapOf() - runBlocking { + withContext(Dispatchers.IO) { + defaultRepository.create(addResourceTags = true, binaryImage) navigationMenuConfigs.resourceReferenceToBitMap( fhirEngine = fhirEngine, decodedImageMap = decodedImageMap, @@ -703,9 +705,12 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Test fun testImageBitmapUpdatedCorrectlyGivenProfileConfiguration(): Unit = runTest { - defaultRepository.create(addResourceTags = true, binaryImage) val decodedImageMap = mutableStateMapOf() - profileConfiguration.views.decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + withContext(Dispatchers.IO) { + fhirEngine.create(binaryImage) + profileConfiguration.views.decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + } + Assert.assertTrue(decodedImageMap.isNotEmpty()) Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) } @@ -713,9 +718,11 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Test fun testImageBitmapUpdatedCorrectlyGivenCardViewProperties(): Unit = runTest { val cardViewProperties = profileConfiguration.views[0] as CardViewProperties - defaultRepository.create(addResourceTags = true, binaryImage) val decodedImageMap = mutableStateMapOf() - listOf(cardViewProperties).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + withContext(Dispatchers.IO) { + defaultRepository.create(addResourceTags = true, binaryImage) + listOf(cardViewProperties).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + } Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } @@ -723,9 +730,12 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Test fun testImageBitmapUpdatedCorrectlyGivenListViewProperties(): Unit = runTest { val cardViewProperties = profileConfiguration.views[0] as CardViewProperties - defaultRepository.create(addResourceTags = true, binaryImage) val decodedImageMap = mutableStateMapOf() - listOf(cardViewProperties.content[0]).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + withContext(Dispatchers.IO) { + defaultRepository.create(addResourceTags = true, binaryImage) + listOf(cardViewProperties.content[0]) + .decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + } Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } @@ -735,9 +745,11 @@ class ConfigExtensionsKtTest : RobolectricTest() { val cardViewProperties = profileConfiguration.views[0] as CardViewProperties val listViewProperties = cardViewProperties.content[0] as ListProperties val decodedImageMap = mutableStateMapOf() - defaultRepository.create(addResourceTags = true, binaryImage) - listOf(listViewProperties.registerCard.views[0]) - .decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + withContext(Dispatchers.IO) { + defaultRepository.create(addResourceTags = true, binaryImage) + listOf(listViewProperties.registerCard.views[0]) + .decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + } Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } @@ -747,9 +759,11 @@ class ConfigExtensionsKtTest : RobolectricTest() { val cardViewProperties = profileConfiguration.views[0] as CardViewProperties val listViewProperties = cardViewProperties.content[0] as ListProperties val columnProperties = listViewProperties.registerCard.views[0] as ColumnProperties - defaultRepository.create(addResourceTags = true, binaryImage) val decodedImageMap = mutableStateMapOf() - listOf(columnProperties.children[0]).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + withContext(Dispatchers.IO) { + defaultRepository.create(addResourceTags = true, binaryImage) + listOf(columnProperties.children[0]).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + } Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) Assert.assertTrue(decodedImageMap.isNotEmpty()) } @@ -773,40 +787,10 @@ class ConfigExtensionsKtTest : RobolectricTest() { ), ) val decodedImageMap = mutableStateMapOf() - listOf(rowProperties).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + withContext(Dispatchers.IO) { + listOf(rowProperties).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) + } Assert.assertTrue(decodedImageMap.isEmpty()) Assert.assertTrue(!decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) } - - @Test(expected = Exception::class) - fun testExceptionCaughtOnDecodingBitmap() = runTest { - val cardViewProperties = profileConfiguration.views[0] as CardViewProperties - val listViewProperties = cardViewProperties.content[0] as ListProperties - val columnProperties = listViewProperties.registerCard.views[0] as ColumnProperties - val rowProperties = - (columnProperties.children[0] as RowProperties).copy( - children = - listOf( - ImageProperties( - imageConfig = - ImageConfig( - type = ICON_TYPE_REMOTE, - reference = "imageReference", - ), - ), - ), - ) - val decodedImageMap = mutableStateMapOf() - - coEvery { defaultRepository.loadResource(anyString()) } returns - Binary().apply { - this.id = "imageReference" - this.contentType = "image/jpeg" - this.data = "gibberish value".toByteArray() - } - - listOf(rowProperties).decodeImageResourcesToBitmap(fhirEngine, decodedImageMap) - Assert.assertTrue(decodedImageMap.isEmpty()) - Assert.assertTrue(!decodedImageMap.containsKey("imageReference")) - } } From 8546693892246f70b6c85b6ce76d1a4bfe13983b Mon Sep 17 00:00:00 2001 From: Elly Kitoto Date: Tue, 17 Sep 2024 19:15:46 +0300 Subject: [PATCH 057/110] Refactor navigation to GeowidgetLauncher workflow Signed-off-by: Elly Kitoto --- .../quest/util/extensions/ConfigExtensions.kt | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt index a3b7a6979d..f4fcdf32f5 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt @@ -30,7 +30,6 @@ import androidx.core.os.bundleOf import androidx.navigation.NavController import androidx.navigation.NavOptions import com.google.android.fhir.FhirEngine -import kotlin.collections.set import org.hl7.fhir.r4.model.Binary import org.smartregister.fhircore.engine.configuration.navigation.ICON_TYPE_REMOTE import org.smartregister.fhircore.engine.configuration.navigation.NavigationMenuConfig @@ -63,6 +62,7 @@ import org.smartregister.fhircore.quest.navigation.NavigationArg import org.smartregister.fhircore.quest.ui.pdf.PdfLauncherFragment import org.smartregister.fhircore.quest.ui.shared.QuestionnaireHandler import org.smartregister.p2p.utils.startP2PScreen +import kotlin.collections.set const val PRACTITIONER_ID = "practitionerId" @@ -184,16 +184,23 @@ fun ActionConfig.handleClickEvent( navController.navigate(MainNavigationScreen.Insight.route) ApplicationWorkflow.DEVICE_TO_DEVICE_SYNC -> startP2PScreen(navController.context) ApplicationWorkflow.LAUNCH_MAP -> { - val mapFragmentDestination = MainNavigationScreen.GeoWidgetLauncher.route - - val isMapFragmentExists = navController.currentDestination?.id == mapFragmentDestination - if (isMapFragmentExists) { - navController.popBackStack(mapFragmentDestination, false) + val args = bundleOf(NavigationArg.GEO_WIDGET_ID to actionConfig.id) + // If value != null, we are navigating FROM a map; disallow same map navigation + val currentGeoWidgetId = + navController.currentBackStackEntry?.arguments?.getString(NavigationArg.GEO_WIDGET_ID) + val sameGeoWidgetNavigation = + args.getString(NavigationArg.GEO_WIDGET_ID) == + navController.previousBackStackEntry?.arguments?.getString(NavigationArg.GEO_WIDGET_ID) + if (!currentGeoWidgetId.isNullOrEmpty() && sameGeoWidgetNavigation) { + return } else { navController.navigate( - resId = mapFragmentDestination, - args = bundleOf(NavigationArg.GEO_WIDGET_ID to actionConfig.id), - navOptions = navOptions(mapFragmentDestination, inclusive = true, singleOnTop = true), + resId = MainNavigationScreen.GeoWidgetLauncher.route, + args = args, + navOptions = + navController.currentDestination?.id?.let { + navOptions(resId = it, inclusive = actionConfig.popNavigationBackStack == true) + }, ) } } From 81e8b7dd12a8aac76a2efa455c27cce29efdc52a Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:49:20 +0300 Subject: [PATCH 058/110] Use dipatcher IO to handle questionnaire submission --- .../fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 472effbc78..33c108e27f 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -166,7 +166,7 @@ constructor( context: Context, onSuccessfulSubmission: (List, QuestionnaireResponse) -> Unit, ) { - viewModelScope.launch(SupervisorJob()) { + viewModelScope.launch(dispatcherProvider.io() + SupervisorJob()) { val questionnaireResponseValid = validateQuestionnaireResponse( questionnaire = questionnaire, @@ -672,7 +672,7 @@ constructor( * has an answer. */ fun saveDraftQuestionnaire(questionnaireResponse: QuestionnaireResponse) { - viewModelScope.launch { + viewModelScope.launch(dispatcherProvider.io()) { val questionnaireHasAnswer = questionnaireResponse.item.any { it.answer.any { answerComponent -> answerComponent.hasValue() } From bd68521f81fa370a1224617261e1680f740aee6a Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:20:14 +0300 Subject: [PATCH 059/110] Hotfix space_asterisk --- android/quest/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/quest/src/main/res/values/strings.xml b/android/quest/src/main/res/values/strings.xml index 9a21e34727..adb04213d9 100644 --- a/android/quest/src/main/res/values/strings.xml +++ b/android/quest/src/main/res/values/strings.xml @@ -132,5 +132,5 @@ Scan QR Code Place your camera over the entire QR Code to start scanning Failed to get GPS location - \\u0020\\u002a + \u0020\u002a From 61a2abbfcd0431e9e1daacf9a6dbf68cf96692b5 Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:25:16 +0300 Subject: [PATCH 060/110] Upgrade engine to v1.0.0-preview14.1-SNAPSHOT --- android/gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 4a03a8bdc5..d5a71a5449 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -27,7 +27,7 @@ fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview14-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview14-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview14.1-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" fhir-sdk-workflow = "0.1.0-alpha04-preview13-SNAPSHOT" foundation = "1.6.8" From 27c5f6473af5dc6fe525959339b6f01fe987f20c Mon Sep 17 00:00:00 2001 From: Simon Njoroge Date: Wed, 18 Sep 2024 16:39:27 +0300 Subject: [PATCH 061/110] Update infiniteScroll to not be default --- .../engine/configuration/register/RegisterConfiguration.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt index b4a82d4cbb..7f6d5c7ddd 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/register/RegisterConfiguration.kt @@ -50,7 +50,7 @@ data class RegisterConfiguration( val filterDataByRelatedEntityLocation: Boolean = false, val topScreenSection: TopScreenSectionConfig? = null, val onSearchByQrSingleResultActions: List? = null, - val infiniteScroll: Boolean = true, + val infiniteScroll: Boolean = false, ) : Configuration() { val onSearchByQrSingleResultValidActions = onSearchByQrSingleResultActions?.filter { it.trigger == ActionTrigger.ON_SEARCH_SINGLE_RESULT } From 39c1db61163399dcad765608c603fbcf33b9f97f Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:38:37 +0300 Subject: [PATCH 062/110] Update sdk versions --- android/buildSrc/src/main/kotlin/BuildConfigs.kt | 2 +- android/gradle/libs.versions.toml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/buildSrc/src/main/kotlin/BuildConfigs.kt b/android/buildSrc/src/main/kotlin/BuildConfigs.kt index 78176a7a61..fe8057f58e 100644 --- a/android/buildSrc/src/main/kotlin/BuildConfigs.kt +++ b/android/buildSrc/src/main/kotlin/BuildConfigs.kt @@ -3,7 +3,7 @@ object BuildConfigs { const val compileSdk = 34 const val targetSdk = 34 const val versionCode = 11 - const val versionName = "2.0.0" + const val versionName = "2.0.0.2" const val applicationId = "org.smartregister.opensrp" const val jvmToolchain = 17 const val kotlinCompilerExtensionVersion = "1.5.8" diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index d5a71a5449..101912c086 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -24,12 +24,12 @@ dokkaBase = "1.9.20" easyRulesCore = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" -fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" -fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview14-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview14.1-SNAPSHOT" -fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview13-SNAPSHOT" +fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-rc1-SNAPSHOT" +fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-rc1-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview14-rc1-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview14-rc3-SNAPSHOT" +fhir-sdk-knowledge = "0.1.0-alpha03-preview5-rc1-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview10-rc1-SNAPSHOT" foundation = "1.6.8" fragment-ktx = "1.8.2" glide = "4.16.0" From aad8b61b5520c48aae753d9d9ca58656dfe0e062 Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:45:03 +0300 Subject: [PATCH 063/110] Update integration Faker to fix error --- .../org/smartregister/fhircore/quest/integration/Faker.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt b/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt index 1350636997..9666c7a8fb 100644 --- a/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt +++ b/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt @@ -18,6 +18,7 @@ package org.smartregister.fhircore.quest.integration import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry +import com.google.android.fhir.CrudFhirEngine import com.google.android.fhir.FhirEngine import com.google.android.fhir.LocalChange import com.google.android.fhir.SearchResult @@ -109,6 +110,10 @@ object Faker { } override suspend fun update(vararg resource: Resource) {} + + override suspend fun withTransaction(block: suspend CrudFhirEngine.() -> Unit) { + // TODO("Not yet implemented") + } } val fhirResourceService = From ef67328f410ceb9b34b017f0d6163377c888bd2b Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:07:42 +0300 Subject: [PATCH 064/110] Revert upgrade on workflow and knowledge libs --- android/gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 101912c086..a4261131df 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -28,8 +28,8 @@ fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-rc1-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-rc1-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview14-rc1-SNAPSHOT" fhir-sdk-engine = "1.0.0-preview14-rc3-SNAPSHOT" -fhir-sdk-knowledge = "0.1.0-alpha03-preview5-rc1-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview10-rc1-SNAPSHOT" +fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview13-SNAPSHOT" foundation = "1.6.8" fragment-ktx = "1.8.2" glide = "4.16.0" From c04024fdf4ed6d5eee6e953c9cd16c8fcbf55b13 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 31 Jul 2024 12:27:20 +0300 Subject: [PATCH 065/110] =?UTF-8?q?Upgrade=20FHIR=20SDK=20depenencies=20?= =?UTF-8?q?=E2=AC=86=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../engine/task/WorkflowCarePlanGenerator.kt | 7 ++- .../util/extension/ResourceExtension.kt | 17 -------- .../util/extension/ResourceExtensionTest.kt | 43 ------------------- android/gradle/libs.versions.toml | 25 +++++------ 4 files changed, 16 insertions(+), 76 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt index c40b1a092a..641edb2f42 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt @@ -41,7 +41,8 @@ import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.Task import org.hl7.fhir.r4.utils.FHIRPathEngine import org.opencds.cqf.fhir.cql.LibraryEngine -import org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor +import org.opencds.cqf.fhir.cr.plandefinition.PlanDefinitionProcessor +import org.opencds.cqf.fhir.utility.monad.Eithers import org.opencds.cqf.fhir.utility.r4.Parameters.part import org.smartregister.fhircore.engine.data.local.DefaultRepository import timber.log.Timber @@ -128,9 +129,7 @@ constructor( params.addParameter(part("%subject", subject)) return planDefProcessor.apply( - null, - null, - planDefinition, + Eithers.forRight3(planDefinition), "Patient/${subject.id}", null, null, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt index 9c08e1e1a4..e7281e8f27 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt @@ -20,7 +20,6 @@ import android.content.Context import ca.uhn.fhir.context.FhirContext import ca.uhn.fhir.parser.IParser import ca.uhn.fhir.rest.gclient.ReferenceClientParam -import com.google.android.fhir.datacapture.extensions.createQuestionnaireResponseItem import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.get import com.google.android.fhir.search.search @@ -187,22 +186,6 @@ fun JSONObject.updateFrom(updated: JSONObject) { keys.forEach { key -> updated.opt(key)?.run { put(key, this) } } } -fun QuestionnaireResponse.generateMissingItems(questionnaire: Questionnaire) = - questionnaire.item.generateMissingItems(this.item) - -fun List.generateMissingItems( - qrItems: MutableList, -) { - this.forEachIndexed { index, qItem -> - // generate complete hierarchy if response item missing otherwise check for nested items - if (qrItems.isEmpty() || (index < qrItems.size && qItem.linkId != qrItems[index].linkId)) { - qrItems.add(index, qItem.createQuestionnaireResponseItem()) - } else if (index < qrItems.size) { - qItem.item.generateMissingItems(qrItems[index].item) - } - } -} - /** * Set all questions that are not of type [Questionnaire.QuestionnaireItemType.GROUP] to readOnly if * [readOnly] is true. This also generates the correct FHIRPath population expression for each diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt index 1fb5145f05..2a11213eb1 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt @@ -18,9 +18,6 @@ package org.smartregister.fhircore.engine.util.extension import android.app.Application import androidx.test.core.app.ApplicationProvider -import ca.uhn.fhir.context.FhirContext -import ca.uhn.fhir.context.FhirVersionEnum -import ca.uhn.fhir.parser.IParser import com.google.android.fhir.datacapture.extensions.logicalId import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest @@ -691,46 +688,6 @@ class ResourceExtensionTest : RobolectricTest() { ) } - @Test - fun testGenerateMissingItemsFromQuestionnaireShouldNotThrowException() { - val patientRegistrationQuestionnaire = - "register-patient-missingitems/missingitem-questionnaire.json".readFile() - val patientRegistrationQuestionnaireResponse = - "register-patient-missingitems/missingitem-questionnaire-response.json".readFile() - val iParser: IParser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser() - val questionnaire = - iParser.parseResource(Questionnaire::class.java, patientRegistrationQuestionnaire) - val questionnaireResponse = - iParser.parseResource( - QuestionnaireResponse::class.java, - patientRegistrationQuestionnaireResponse, - ) - - questionnaire.item.generateMissingItems(questionnaireResponse.item) - - Assert.assertTrue(questionnaireResponse.item.size <= questionnaire.item.size) - } - - @Test - fun testGenerateMissingItemsFromQuestionnaireResponseShouldNotThrowException() { - val patientRegistrationQuestionnaire = - "register-patient-missingitems/missingitem-questionnaire.json".readFile() - val patientRegistrationQuestionnaireResponse = - "register-patient-missingitems/missingitem-questionnaire-response.json".readFile() - val iParser: IParser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser() - val questionnaire = - iParser.parseResource(Questionnaire::class.java, patientRegistrationQuestionnaire) - val questionnaireResponse = - iParser.parseResource( - QuestionnaireResponse::class.java, - patientRegistrationQuestionnaireResponse, - ) - - questionnaireResponse.generateMissingItems(questionnaire) - - Assert.assertTrue(questionnaireResponse.item.size <= questionnaire.item.size) - } - @Test fun `prepareQuestionsForReadingOrEditing should set readOnly to true when passed`() { val questionnaire = Questionnaire() diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 4713965fe4..2aabde2973 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -17,32 +17,31 @@ converter-gson = "2.9.0" core-ktx = "1.13.1" core-testing = "2.2.0" coverallsGradlePlugin = "2.12.2" -cqfFhirCr = "3.0.0-PRE9" +cqfFhirCr = "3.8.0" dagger-hilt = "2.51" datastore = "1.1.1" desugar-jdk-libs = "2.0.4" -dokkaBase = "1.8.20" +dokkaBase = "1.9.20" easy-rules-jexl = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview12-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview11.3-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview13-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview12-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview10-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview11-SNAPSHOT" foundation = "1.6.8" -fragment-ktx = "1.8.1" +fragment-ktx = "1.8.2" glide = "4.16.0" gradle = "8.3.2" gson = "2.10.1" hilt = "1.2.0" jetbrains = "1.9.20" -jetbrains-kotlin-jvm="1.9.22" jjwt = "0.9.1" joda-time = "2.10.14" json = "20230618" -jsonPath = "2.8.0" +jsonPath = "2.9.0" junit = "1.2.1" junit-jupiter = "5.10.3" junit-ktx = "1.2.1" @@ -54,7 +53,8 @@ kt3k-coveralls-ver="2.12.0" ktlint = "0.50.0" kujaku-library = "0.10.5-SNAPSHOT" leakcanary-android = "2.10" -lifecycle= "2.8.3" +lifecycle= "2.8.4" +logback-android = "3.0.0" mapbox-sdk-turf = "4.8.0" material = "1.12.0" mlkit-barcode-scanning = "17.3.0" @@ -74,10 +74,10 @@ prettytime = "5.0.2.Final" retrofit = "2.9.0" retrofit-mock = "2.9.0" retrofit2-kotlinx-serialization-converter = "0.8.0" -robolectric = "4.10.3" +robolectric = "4.13" rules = "1.6.1" security-crypto = "1.1.0-alpha06" -slf4j-nop = "1.7.36" +slf4j-nop = "2.0.7" spotlessPluginGradle = "6.25.0" stax-api = "1.0-2" timber = "5.0.1" @@ -159,6 +159,7 @@ leakcanary-android = { group = "com.squareup.leakcanary", name = "leakcanary-and lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" } lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" } +logback-android = { module = "com.github.tony19:logback-android", version.ref = "logback-android" } mapbox-sdk-turf = { group = "com.mapbox.mapboxsdk", name = "mapbox-sdk-turf", version.ref = "mapbox-sdk-turf" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } mlkit-barcode-scanning = { group = "com.google.mlkit", name = "barcode-scanning", version.ref = "mlkit-barcode-scanning"} @@ -209,7 +210,7 @@ dagger-hilt-android= { id = "com.google.dagger.hilt.android", version.ref = "dag kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-serialization" } kt3k-coveralls = { id = "com.github.kt3k.coveralls", version.ref = "kt3k-coveralls-ver" } org-jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "jetbrains" } -org-jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrains-kotlin-jvm" } +org-jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } org-owasp-dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "owasp" } [bundles] From e2da55e671697fc1645aa7b2413627bcd92998d0 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Thu, 8 Aug 2024 18:57:59 +0300 Subject: [PATCH 066/110] Upragde SDC library --- android/gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 2aabde2973..d01f88fe44 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -27,7 +27,7 @@ espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview13-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview13.1-SNAPSHOT" fhir-sdk-engine = "1.0.0-preview12-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" fhir-sdk-workflow = "0.1.0-alpha04-preview11-SNAPSHOT" From f6bbada7888414d466c598828980f7e52a834dac Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 9 Aug 2024 16:04:15 +0300 Subject: [PATCH 067/110] Replace JWT token parser library --- android/engine/build.gradle.kts | 4 ++-- .../data/remote/shared/TokenAuthenticator.kt | 15 +++++++-------- .../engine/auth/TokenAuthenticatorTest.kt | 4 ++-- android/gradle/libs.versions.toml | 10 +++++----- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index 7393796830..f70ab00aa9 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { `jacoco-report` - `ktlint` + ktlint id("com.android.library") id("kotlin-android") id("kotlin-kapt") @@ -162,7 +162,7 @@ dependencies { api(libs.glide) api(libs.knowledge) { exclude(group = "org.slf4j", module = "jcl-over-slf4j") } api(libs.p2p.lib) - api(libs.jjwt) + api(libs.java.jwt) api(libs.fhir.common.utils) { exclude(group = "org.slf4j", module = "jcl-over-slf4j") } api(libs.runtime.livedata) api(libs.foundation) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt index 0f82f47883..ab3d7f305c 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/shared/TokenAuthenticator.kt @@ -28,11 +28,12 @@ import android.os.Handler import android.os.Looper import android.os.Message import androidx.core.os.bundleOf +import com.auth0.jwt.JWT +import com.auth0.jwt.exceptions.JWTDecodeException +import com.auth0.jwt.interfaces.DecodedJWT import com.google.android.fhir.sync.HttpAuthenticationMethod import com.google.android.fhir.sync.HttpAuthenticator as FhirAuthenticator import dagger.hilt.android.qualifiers.ApplicationContext -import io.jsonwebtoken.JwtException -import io.jsonwebtoken.Jwts import java.io.IOException import java.net.UnknownHostException import java.util.Base64 @@ -62,7 +63,6 @@ constructor( @ApplicationContext val context: Context, ) : FhirAuthenticator { - private val jwtParser = Jwts.parser() private val authConfiguration by lazy { configService.provideAuthConfiguration() } private var isLoginPageRendered = false @@ -131,12 +131,11 @@ constructor( /** This function checks if token is null or empty or expired */ fun isTokenActive(authToken: String?): Boolean { if (authToken.isNullOrEmpty()) return false - val tokenPart = authToken.substringBeforeLast('.').plus(".") return try { - val body = jwtParser.parseClaimsJwt(tokenPart).body - body.expiration.after(today()) - } catch (jwtException: JwtException) { - false + val jwt: DecodedJWT? = JWT.decode(authToken) + jwt?.expiresAt!!.after(today()) + } catch (e: JWTDecodeException) { + return false } } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/auth/TokenAuthenticatorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/auth/TokenAuthenticatorTest.kt index 1aee73852b..35a3bcc403 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/auth/TokenAuthenticatorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/auth/TokenAuthenticatorTest.kt @@ -24,10 +24,10 @@ import android.accounts.OperationCanceledException import android.os.Bundle import androidx.core.os.bundleOf import androidx.test.core.app.ApplicationProvider +import com.auth0.jwt.exceptions.JWTDecodeException import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltTestApplication -import io.jsonwebtoken.JwtException import io.mockk.coEvery import io.mockk.every import io.mockk.just @@ -108,7 +108,7 @@ class TokenAuthenticatorTest : RobolectricTest() { } @Test - @Throws(JwtException::class) + @Throws(JWTDecodeException::class) fun testIsTokenActiveWithExpiredJwtToken() { Assert.assertFalse(tokenAuthenticator.isTokenActive("expired-token")) } diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index d01f88fe44..f79ae0326a 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -3,8 +3,8 @@ accompanist = "0.23.1" activity-compose = "1.8.2" androidJunit5 = "1.8.2.1" androidx-camera = "1.4.0-rc01" -androidx-paging = "3.3.0" -androidx-test = "1.6.1" +androidx-paging = "3.3.2" +androidx-test= "1.6.1" appcompat = "1.7.0" benchmark-junit = "1.2.4" cardview = "1.0.0" @@ -37,8 +37,8 @@ glide = "4.16.0" gradle = "8.3.2" gson = "2.10.1" hilt = "1.2.0" +java-jwt = "4.4.0" jetbrains = "1.9.20" -jjwt = "0.9.1" joda-time = "2.10.14" json = "20230618" jsonPath = "2.9.0" @@ -83,7 +83,7 @@ stax-api = "1.0-2" timber = "5.0.1" ui = "1.6.3" uiautomator = "2.3.0" -work = "2.9.0" +work = "2.9.1" xercesImpl = "2.12.2" [libraries] @@ -134,7 +134,7 @@ gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hilt" } hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hilt" } -jjwt = { group = "io.jsonwebtoken", name = "jjwt", version.ref = "jjwt" } +java-jwt = { module = "com.auth0:java-jwt", version.ref = "java-jwt" } joda-time = { group = "joda-time", name = "joda-time", version.ref = "joda-time" } json = { group = "org.json", name = "json", version.ref = "json" } json-path = { module = "com.jayway.jsonpath:json-path", version.ref = "jsonPath" } From 64c6297b226c2dd42b93d095bb240d626d8bcf1f Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 9 Aug 2024 19:14:18 +0300 Subject: [PATCH 068/110] Fix CQL Content Test --- android/quest/build.gradle.kts | 1 + .../org/smartregister/fhircore/quest/CqlContentTest.kt | 7 +++++-- .../fhircore/quest/robolectric/RobolectricTest.kt | 10 ++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/android/quest/build.gradle.kts b/android/quest/build.gradle.kts index e4dd623b4c..a4ffbeeab1 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -457,6 +457,7 @@ dependencies { testImplementation(libs.navigation.testing) testImplementation(libs.kotlin.test) testImplementation(libs.work.testing) + testImplementation("org.skyscreamer:jsonassert:1.5.3") // To run only on debug builds debugImplementation(libs.ui.test.manifest) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index d9724ab580..7705221fad 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -24,6 +24,7 @@ import dagger.hilt.android.testing.HiltAndroidTest import java.io.File import javax.inject.Inject import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.Condition import org.hl7.fhir.r4.model.Library import org.hl7.fhir.r4.model.Parameters import org.hl7.fhir.r4.model.Resource @@ -32,6 +33,8 @@ import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test +import org.skyscreamer.jsonassert.JSONAssert +import org.skyscreamer.jsonassert.JSONCompareMode import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.valueToString @@ -233,8 +236,8 @@ class CqlContentTest : RobolectricTest() { .replaceTimePart() println(cqlResultStr) - println(expectedResource as String) + println(expectedResource) - Assert.assertEquals(expectedResource, cqlResultStr) + JSONAssert.assertEquals(expectedResource, cqlResultStr, JSONCompareMode.STRICT) } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt index 2e3a93709a..7ef8fa6368 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt @@ -106,7 +106,7 @@ abstract class RobolectricTest { .replace("#NOW", DateTimeType.now().valueAsString) .let { FhirContext.forCached(FhirVersionEnum.R4).newJsonParser().parseResource(it) } - fun IBaseResource.convertToString(trimTime: Boolean) = + fun IBaseResource.convertToString(trimTime: Boolean): String = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser().encodeResourceToString(this).let { // replace time part 11:11:11+05:00 with xx:xx:xx+xx:xx if (trimTime) { @@ -119,8 +119,9 @@ abstract class RobolectricTest { fun String.replaceTimePart() = // replace time part 11:11:11+05:00 with xx:xx:xx+xx:xx // replace time part 11:11:11.111+05:00 with xx:xx:xx+xx:xx - this.replace(Regex("\\d{2}:\\d{2}:\\d{2}.\\d{2}:\\d{2}"), "xx:xx:xx+xx:xx") - .replace(Regex("\\d{2}:\\d{2}:\\d{2}.\\d{3}.\\d{2}:\\d{2}"), "xx:xx:xx+xx:xx") + // replace time part 18:33:04.520481+03:00 + this.replace(Regex("\\d{2}:\\d{2}:\\d{2}.\\d[0-9,+]+:\\d{2}"), "xx:xx:xx+xx:xx") + .replace(Regex("\\d{2}:\\d{2}:\\d{2}.\\d{3}.\\d[0-9,+]+:\\d{2}"), "xx:xx:xx+xx:xx") fun buildStructureMapUtils(): StructureMapUtilities { val pcm = FilesystemPackageCacheManager(true) @@ -136,7 +137,8 @@ abstract class RobolectricTest { return StructureMapUtilities(contextR4, transformSupportServices) } - fun StructureMapUtilities.worker(): IWorkerContext = ReflectionHelpers.getField(this, "worker") + private fun StructureMapUtilities.worker(): IWorkerContext = + ReflectionHelpers.getField(this, "worker") fun transform( scu: StructureMapUtilities, From 7e616ac0ad8ce00d1afafaa4492bdec05971d3ba Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 13 Aug 2024 10:22:03 +0300 Subject: [PATCH 069/110] =?UTF-8?q?Fix=20unit=20tests=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/measure/MeasureReportViewModel.kt | 4 ++- .../quest/data/QuestXFhirQueryResolverTest.kt | 2 +- .../QuestionnaireActivityTest.kt | 26 ++++++++++--------- .../measure/MeasureReportViewModelTest.kt | 20 +++++++++++++- .../util/extensions/ConfigExtensionsKtTest.kt | 9 ++++--- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt index 29b192dcd0..b44219ffd4 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt @@ -343,8 +343,10 @@ constructor( } } + val measureReportPopulationResultList = + formatPopulationMeasureReports(result, reportConfigurations) _measureReportPopulationResultList.addAll( - formatPopulationMeasureReports(result, reportConfigurations), + measureReportPopulationResultList, ) } .onSuccess { diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/QuestXFhirQueryResolverTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/QuestXFhirQueryResolverTest.kt index cade896cb2..94390c95a8 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/QuestXFhirQueryResolverTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/QuestXFhirQueryResolverTest.kt @@ -47,7 +47,7 @@ class QuestXFhirQueryResolverTest : RobolectricTest() { @Test fun testQuestXFhirQueryResolver() = runTest(timeout = 120.seconds) { - val patient = Patient() + val patient = Patient().apply { setActive(true) } val task = Task() fhirEngine.create(patient, task) val xFhirResolver = QuestXFhirQueryResolver(fhirEngine) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt index 5d36f3a84e..e963a2fdf2 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt @@ -45,7 +45,7 @@ import junit.framework.TestCase.assertTrue import kotlin.test.assertNotNull import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Enumerations @@ -70,6 +70,7 @@ import org.smartregister.fhircore.engine.domain.model.ActionParameterType import org.smartregister.fhircore.engine.domain.model.RuleConfig import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.decodeResourceFromString +import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.quest.R import org.smartregister.fhircore.quest.app.fakes.Faker import org.smartregister.fhircore.quest.robolectric.RobolectricTest @@ -184,18 +185,20 @@ class QuestionnaireActivityTest : RobolectricTest() { setupActivity() Assert.assertTrue(questionnaireActivity.supportFragmentManager.fragments.isNotEmpty()) - val firstFragment = questionnaireActivity.supportFragmentManager.fragments.firstOrNull() + val firstFragment = + questionnaireActivity.supportFragmentManager.fragments[ + questionnaireActivity.supportFragmentManager.fragments.size - 1, + ] Assert.assertTrue(firstFragment is QuestionnaireFragment) // Questionnaire should be the same val fragmentQuestionnaire = - questionnaireActivity.supportFragmentManager.fragments - .firstOrNull() + firstFragment ?.arguments ?.getString("questionnaire") ?.decodeResourceFromString() - Assert.assertEquals(questionnaire.id, fragmentQuestionnaire?.id) + Assert.assertEquals(questionnaire.id, fragmentQuestionnaire?.id!!.extractLogicalIdUuid()) val sortedQuestionnaireItemLinkIds = questionnaire.item.map { it.linkId }.sorted().joinToString(",") val sortedFragmentQuestionnaireItemLinkIds = @@ -205,13 +208,12 @@ class QuestionnaireActivityTest : RobolectricTest() { } @Test - fun testThatOnBackPressShowsConfirmationAlertDialog() = - runTest(UnconfinedTestDispatcher()) { - setupActivity() - questionnaireActivity.onBackPressedDispatcher.onBackPressed() - val dialog = shadowOf(ShadowAlertDialog.getLatestAlertDialog()) - Assert.assertNotNull(dialog) - } + fun testThatOnBackPressShowsConfirmationAlertDialog() = runBlocking { + setupActivity() + questionnaireActivity.onBackPressedDispatcher.onBackPressed() + val dialog = shadowOf(ShadowAlertDialog.getLatestAlertDialog()) + Assert.assertNotNull(dialog) + } @Test fun `setupLocationServices should open location settings if location is disabled`() { diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt index 5cb739577d..886c4d3544 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt @@ -47,6 +47,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNotNull import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -90,6 +91,7 @@ import org.smartregister.fhircore.quest.data.report.measure.MeasureReportPagingS import org.smartregister.fhircore.quest.data.report.measure.MeasureReportRepository import org.smartregister.fhircore.quest.navigation.MeasureReportNavigationScreen import org.smartregister.fhircore.quest.robolectric.RobolectricTest +import org.smartregister.fhircore.quest.ui.report.measure.models.MeasureReportPopulationResult import org.smartregister.fhircore.quest.ui.shared.models.MeasureReportSubjectViewData import org.smartregister.fhircore.quest.util.mappers.MeasureReportSubjectViewDataMapper @@ -146,6 +148,8 @@ class MeasureReportViewModelTest : RobolectricTest() { measureReportRepository = measureReportRepository, ), ) + + every { measureReportViewModel.dispatcherProvider.io() } returns Dispatchers.IO } @Test @@ -326,7 +330,7 @@ class MeasureReportViewModelTest : RobolectricTest() { ) coEvery { measureReportViewModel.formatPopulationMeasureReports(any(), any()) } returns - emptyList() + listOf(MeasureReportPopulationResult()) coEvery { fhirEngine.retrievePreviouslyGeneratedMeasureReports( @@ -337,6 +341,20 @@ class MeasureReportViewModelTest : RobolectricTest() { ) } returns listOf(testMeasureReport) + coEvery { + measureReportRepository.evaluatePopulationMeasure( + startDateFormatted = any(), + endDateFormatted = any(), + measureUrl = any(), + subjects = any(), + existing = any(), + practitionerId = any(), + ) + } returns listOf(testMeasureReport) + + coEvery { measureReportRepository.fetchSubjects(any(ReportConfiguration::class)) } returns + listOf() + measureReportViewModel.reportTypeSelectorUiState.value = ReportTypeSelectorUiState(startDate = "21 Jan, 2022", endDate = "27 Jan, 2022") measureReportViewModel.reportConfigurations.add(reportConfiguration) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt index df3750e8f3..55d6071f99 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensionsKtTest.kt @@ -701,15 +701,16 @@ class ConfigExtensionsKtTest : RobolectricTest() { @Test fun testImageBitmapUpdatedCorrectlyGivenProfileConfiguration(): Unit = runTest { defaultRepository.create(addResourceTags = true, binaryImage) - val decodedImageMap = mutableStateMapOf() loadRemoteImagesBitmaps( profileConfiguration.views, registerRepository = registerRepository, computedValuesMap = emptyMap(), configurationRegistry.decodedImageMap, ) - Assert.assertTrue(decodedImageMap.isNotEmpty()) - Assert.assertTrue(decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) + Assert.assertTrue(configurationRegistry.decodedImageMap.isNotEmpty()) + Assert.assertTrue( + configurationRegistry.decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077"), + ) } @Test @@ -806,7 +807,7 @@ class ConfigExtensionsKtTest : RobolectricTest() { Assert.assertTrue(!decodedImageMap.containsKey("d60ff460-7671-466a-93f4-c93a2ebf2077")) } - @Test(expected = Exception::class) + @Test fun testExceptionCaughtOnDecodingBitmap() = runTest { val cardViewProperties = profileConfiguration.views[0] as CardViewProperties val listViewProperties = cardViewProperties.content[0] as ListProperties From 4f3c74cbf212573585425be56cd3c9286febfdf7 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 13 Aug 2024 11:47:24 +0300 Subject: [PATCH 070/110] Refactor to remove Dispatch Provider --- .../engine/data/local/DefaultRepository.kt | 322 ++++++++---------- .../data/local/register/RegisterRepository.kt | 68 ++-- .../fhircore/engine/FhirExtractionTest.kt | 2 - .../data/local/DefaultRepositoryTest.kt | 6 - .../local/register/RegisterRepositoryTest.kt | 4 - .../engine/task/FhirCarePlanGeneratorTest.kt | 4 - .../task/FhirResourceExpireWorkerTest.kt | 3 +- .../report/measure/MeasureReportRepository.kt | 42 +-- .../geowidget/GeoWidgetLauncherViewModel.kt | 2 - .../fhircore/quest/data/DataMigrationTest.kt | 26 +- .../measure/MeasureReportPagingSourceTest.kt | 2 - .../measure/MeasureReportRepositoryTest.kt | 3 - .../GeoWidgetLauncherViewModelTest.kt | 4 - .../quest/ui/profile/ProfileViewModelTest.kt | 2 - .../QuestionnaireActivityTest.kt | 4 - .../QuestionnaireViewModelTest.kt | 3 +- 16 files changed, 220 insertions(+), 277 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt index fd6310ec06..3eb1c428a1 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt @@ -46,7 +46,6 @@ import java.util.LinkedList import java.util.UUID import javax.inject.Inject import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull @@ -83,7 +82,6 @@ import org.smartregister.fhircore.engine.domain.model.ResourceFilterExpression import org.smartregister.fhircore.engine.domain.model.RuleConfig import org.smartregister.fhircore.engine.domain.model.SortConfig import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.encodeResourceToString @@ -103,7 +101,6 @@ open class DefaultRepository @Inject constructor( open val fhirEngine: FhirEngine, - open val dispatcherProvider: DispatcherProvider, open val sharedPreferencesHelper: SharedPreferencesHelper, open val configurationRegistry: ConfigurationRegistry, open val configService: ConfigService, @@ -114,17 +111,15 @@ constructor( ) { suspend inline fun loadResource(resourceId: String): T? { - return withContext(dispatcherProvider.io()) { fhirEngine.loadResource(resourceId) } + return fhirEngine.loadResource(resourceId) } suspend fun loadResource(resourceId: String, resourceType: ResourceType): Resource = - withContext(dispatcherProvider.io()) { fhirEngine.get(resourceType, resourceId) } + fhirEngine.get(resourceType, resourceId) suspend fun loadResource(reference: Reference) = - withContext(dispatcherProvider.io()) { - IdType(reference.reference).let { - fhirEngine.get(ResourceType.fromCode(it.resourceType), it.idPart) - } + IdType(reference.reference).let { + fhirEngine.get(ResourceType.fromCode(it.resourceType), it.idPart) } suspend inline fun searchResourceFor( @@ -134,19 +129,17 @@ constructor( dataQueries: List = listOf(), configComputedRuleValues: Map, ): List = - withContext(dispatcherProvider.io()) { - fhirEngine - .search { - filterByResourceTypeId(token, subjectType, subjectId) - dataQueries.forEach { - filterBy( - dataQuery = it, - configComputedRuleValues = configComputedRuleValues, - ) - } + fhirEngine + .search { + filterByResourceTypeId(token, subjectType, subjectId) + dataQueries.forEach { + filterBy( + dataQuery = it, + configComputedRuleValues = configComputedRuleValues, + ) } - .map { it.resource } - } + } + .map { it.resource } suspend inline fun search(search: Search) = fhirEngine.search(search).map { it.resource } @@ -161,17 +154,13 @@ constructor( * param [addResourceTags] */ suspend fun create(addResourceTags: Boolean = true, vararg resource: Resource): List { - return withContext(dispatcherProvider.io()) { - preProcessResources(addResourceTags, *resource) - fhirEngine.create(*resource) - } + preProcessResources(addResourceTags, *resource) + return fhirEngine.create(*resource) } suspend fun createRemote(addResourceTags: Boolean = true, vararg resource: Resource) { - return withContext(dispatcherProvider.io()) { - preProcessResources(addResourceTags, *resource) - fhirEngine.create(*resource, isLocalOnly = true) - } + preProcessResources(addResourceTags, *resource) + fhirEngine.create(*resource, isLocalOnly = true) } private fun preProcessResources(addResourceTags: Boolean, vararg resource: Resource) { @@ -197,23 +186,19 @@ constructor( resourceId: String, softDelete: Boolean = false, ) { - withContext(dispatcherProvider.io()) { - if (softDelete) { - val resource = fhirEngine.get(resourceType, resourceId) - softDelete(resource) - } else { - fhirEngine.delete(resourceType, resourceId) - } + if (softDelete) { + val resource = fhirEngine.get(resourceType, resourceId) + softDelete(resource) + } else { + fhirEngine.delete(resourceType, resourceId) } } suspend fun delete(resource: Resource, softDelete: Boolean = false) { - withContext(dispatcherProvider.io()) { - if (softDelete) { - softDelete(resource) - } else { - fhirEngine.delete(resource.resourceType, resource.logicalId) - } + if (softDelete) { + softDelete(resource) + } else { + fhirEngine.delete(resource.resourceType, resource.logicalId) } } @@ -242,24 +227,20 @@ constructor( * param [addMandatoryTags] */ suspend fun addOrUpdate(addMandatoryTags: Boolean = true, resource: R) { - return withContext(dispatcherProvider.io()) { - resource.updateLastUpdated() - try { - fhirEngine.get(resource.resourceType, resource.logicalId).run { - val updateFrom = updateFrom(resource) - fhirEngine.update(updateFrom) - } - } catch (resourceNotFoundException: ResourceNotFoundException) { - create(addMandatoryTags, resource) + resource.updateLastUpdated() + try { + fhirEngine.get(resource.resourceType, resource.logicalId).run { + val updateFrom = updateFrom(resource) + fhirEngine.update(updateFrom) } + } catch (resourceNotFoundException: ResourceNotFoundException) { + create(addMandatoryTags, resource) } } suspend fun update(resource: R) { - return withContext(dispatcherProvider.io()) { - resource.updateLastUpdated() - fhirEngine.update(resource) - } + resource.updateLastUpdated() + fhirEngine.update(resource) } suspend fun loadManagingEntity(group: Group) = @@ -557,7 +538,7 @@ constructor( }, ): Long = kotlin - .runCatching { withContext(dispatcherProvider.io()) { fhirEngine.count(this@count) } } + .runCatching { fhirEngine.count(this@count) } .onSuccess { count -> onSuccess(count) } .onFailure { throwable -> onFailure(throwable) } .getOrDefault(0) @@ -765,63 +746,60 @@ constructor( subject: Resource? = null, eventWorkflow: EventWorkflow, ) { - withContext(dispatcherProvider.io()) { - val configRules = configRulesExecutor.generateRules(resourceConfig.configRules ?: listOf()) - val computedValuesMap = - configRulesExecutor.fireRules(rules = configRules, baseResource = subject).mapValues { - entry, - -> - val initialValue = entry.value.toString() - if (initialValue.contains('/')) { - """${initialValue.substringBefore("/")}/${initialValue.extractLogicalIdUuid()}""" - } else { - initialValue - } - } - - Timber.i("Computed values map = ${computedValuesMap.values}") - val search = - Search(resourceConfig.resource).apply { - applyConfiguredSortAndFilters( - resourceConfig = resourceConfig, - sortData = false, - filterActiveResources = null, - configComputedRuleValues = computedValuesMap, - ) + val configRules = configRulesExecutor.generateRules(resourceConfig.configRules ?: listOf()) + val computedValuesMap = + configRulesExecutor.fireRules(rules = configRules, baseResource = subject).mapValues { entry, + -> + val initialValue = entry.value.toString() + if (initialValue.contains('/')) { + """${initialValue.substringBefore("/")}/${initialValue.extractLogicalIdUuid()}""" + } else { + initialValue } - val resources = fhirEngine.search(search).map { it.resource } - val filteredResources = - filterResourcesByFhirPathExpression( - resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, - resources = resources, - ) - filteredResources.forEach { - Timber.i("Closing Resource type ${it.resourceType.name} and id ${it.id}") - closeResource(resource = it, eventWorkflow = eventWorkflow) } - val retrievedRelatedResources = - retrieveRelatedResources( - resources = resources, - relatedResourcesConfigs = resourceConfig.relatedResources, - relatedResourceWrapper = RelatedResourceWrapper(), + Timber.i("Computed values map = ${computedValuesMap.values}") + val search = + Search(resourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = resourceConfig, + sortData = false, + filterActiveResources = null, configComputedRuleValues = computedValuesMap, ) + } + val resources = fhirEngine.search(search).map { it.resource } + val filteredResources = + filterResourcesByFhirPathExpression( + resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, + resources = resources, + ) + filteredResources.forEach { + Timber.i("Closing Resource type ${it.resourceType.name} and id ${it.id}") + closeResource(resource = it, eventWorkflow = eventWorkflow) + } - retrievedRelatedResources.relatedResourceMap.forEach { resourcesMap -> - val filteredRelatedResources = - filterResourcesByFhirPathExpression( - resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, - resources = resourcesMap.value, - ) + val retrievedRelatedResources = + retrieveRelatedResources( + resources = resources, + relatedResourcesConfigs = resourceConfig.relatedResources, + relatedResourceWrapper = RelatedResourceWrapper(), + configComputedRuleValues = computedValuesMap, + ) - filteredRelatedResources.forEach { resource -> - Timber.i( - "Closing related Resource type ${resource.resourceType.name} and id ${resource.id}", - ) - if (filterRelatedResource(resource, resourceConfig)) { - closeResource(resource = resource, eventWorkflow = eventWorkflow) - } + retrievedRelatedResources.relatedResourceMap.forEach { resourcesMap -> + val filteredRelatedResources = + filterResourcesByFhirPathExpression( + resourceFilterExpressions = eventWorkflow.resourceFilterExpressions, + resources = resourcesMap.value, + ) + + filteredRelatedResources.forEach { resource -> + Timber.i( + "Closing related Resource type ${resource.resourceType.name} and id ${resource.id}", + ) + if (filterRelatedResource(resource, resourceConfig)) { + closeResource(resource = resource, eventWorkflow = eventWorkflow) } } } @@ -917,7 +895,7 @@ constructor( val updatedResource = parser.parseResource(resourceDefinition, updatedResourceDocument.jsonString()) updatedResource.setId(updatedResource.idElement.idPart) - withContext(dispatcherProvider.io()) { fhirEngine.update(updatedResource as Resource) } + fhirEngine.update(updatedResource as Resource) } private fun getJsonContent(jsonElement: JsonElement): Any? { @@ -948,9 +926,7 @@ constructor( suspend fun purge(resource: Resource, forcePurge: Boolean) { try { - withContext(dispatcherProvider.io()) { - fhirEngine.purge(resource.resourceType, resource.logicalId, forcePurge) - } + fhirEngine.purge(resource.resourceType, resource.logicalId, forcePurge) } catch (resourceNotFoundException: ResourceNotFoundException) { Timber.e( "Purge failed -> Resource with ID ${resource.logicalId} does not exist", @@ -968,64 +944,62 @@ constructor( pageSize: Int? = null, configRules: List?, ): List { - return withContext(dispatcherProvider.io()) { - val baseResourceConfig = fhirResourceConfig.baseResource - val relatedResourcesConfig = fhirResourceConfig.relatedResources - val configComputedRuleValues = configRules.configRulesComputedValues() - val search = - Search(type = baseResourceConfig.resource).apply { - applyConfiguredSortAndFilters( - resourceConfig = baseResourceConfig, - filterActiveResources = filterActiveResources, - sortData = true, - configComputedRuleValues = configComputedRuleValues, + val baseResourceConfig = fhirResourceConfig.baseResource + val relatedResourcesConfig = fhirResourceConfig.relatedResources + val configComputedRuleValues = configRules.configRulesComputedValues() + val search = + Search(type = baseResourceConfig.resource).apply { + applyConfiguredSortAndFilters( + resourceConfig = baseResourceConfig, + filterActiveResources = filterActiveResources, + sortData = true, + configComputedRuleValues = configComputedRuleValues, + ) + applyFilterByRelatedEntityLocationMetaTag( + baseResourceConfig.resource, + filterByRelatedEntityLocationMetaTag, + ) + if (currentPage != null && pageSize != null) { + count = pageSize + from = currentPage * pageSize + } + } + + val baseFhirResources = + kotlin + .runCatching { + val searchTime = System.currentTimeMillis() + val result = fhirEngine.search(search) + Timber.w( + "It took ${(System.currentTimeMillis() - searchTime) / 1000} second(s) to search resources of type ${baseResourceConfig.resource}", ) - applyFilterByRelatedEntityLocationMetaTag( - baseResourceConfig.resource, - filterByRelatedEntityLocationMetaTag, + result + } + .onFailure { + Timber.e( + it, + "Error retrieving resources. Empty list returned by default", ) - if (currentPage != null && pageSize != null) { - count = pageSize - from = currentPage * pageSize - } } + .getOrDefault(emptyList()) - val baseFhirResources = - kotlin - .runCatching { - val searchTime = System.currentTimeMillis() - val result = fhirEngine.search(search) - Timber.w( - "It took ${(System.currentTimeMillis() - searchTime) / 1000} second(s) to search resources of type ${baseResourceConfig.resource}", - ) - result - } - .onFailure { - Timber.e( - it, - "Error retrieving resources. Empty list returned by default", - ) - } - .getOrDefault(emptyList()) - - baseFhirResources.map { searchResult -> - val retrievedRelatedResources = - retrieveRelatedResources( - resources = listOf(searchResult.resource), - relatedResourcesConfigs = relatedResourcesConfig, - relatedResourceWrapper = RelatedResourceWrapper(), - configComputedRuleValues = configComputedRuleValues, - ) - val secondaryRepositoryResourceData = - secondaryResourceConfigs.retrieveSecondaryRepositoryResourceData(filterActiveResources) - RepositoryResourceData( - resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, - resource = searchResult.resource, - relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, - relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, - secondaryRepositoryResourceData = secondaryRepositoryResourceData, + return baseFhirResources.map { searchResult -> + val retrievedRelatedResources = + retrieveRelatedResources( + resources = listOf(searchResult.resource), + relatedResourcesConfigs = relatedResourcesConfig, + relatedResourceWrapper = RelatedResourceWrapper(), + configComputedRuleValues = configComputedRuleValues, ) - } + val secondaryRepositoryResourceData = + secondaryResourceConfigs.retrieveSecondaryRepositoryResourceData(filterActiveResources) + RepositoryResourceData( + resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, + resource = searchResult.resource, + relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, + relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, + secondaryRepositoryResourceData = secondaryRepositoryResourceData, + ) } } @@ -1159,18 +1133,16 @@ constructor( } private suspend fun retrieveSubLocations(locationId: String) = - withContext(dispatcherProvider.io()) { - fhirEngine - .search( - Search(type = ResourceType.Location).apply { - filter( - Location.PARTOF, - { value = locationId.asReference(ResourceType.Location).reference }, - ) - }, - ) - .mapTo(LinkedList()) { it.resource } - } + fhirEngine + .search( + Search(type = ResourceType.Location).apply { + filter( + Location.PARTOF, + { value = locationId.asReference(ResourceType.Location).reference }, + ) + }, + ) + .mapTo(LinkedList()) { it.resource } /** * A wrapper data class to hold search results. All related resources are flattened into one Map diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt index d8ae465d39..71405b7344 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepository.kt @@ -22,7 +22,6 @@ import com.google.android.fhir.FhirEngine import com.google.android.fhir.search.Search import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject -import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.Resource import org.smartregister.fhircore.engine.configuration.ConfigType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry @@ -36,7 +35,6 @@ import org.smartregister.fhircore.engine.domain.model.FhirResourceConfig import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData import org.smartregister.fhircore.engine.domain.repository.Repository import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -46,7 +44,6 @@ class RegisterRepository @Inject constructor( override val fhirEngine: FhirEngine, - override val dispatcherProvider: DispatcherProvider, override val sharedPreferencesHelper: SharedPreferencesHelper, override val configurationRegistry: ConfigurationRegistry, override val configService: ConfigService, @@ -58,7 +55,6 @@ constructor( Repository, DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -124,44 +120,42 @@ constructor( fhirResourceConfig: FhirResourceConfig?, paramsList: Array?, ): RepositoryResourceData { - return withContext(dispatcherProvider.io()) { - val paramsMap: Map = - paramsList - ?.asSequence() - ?.filter { - (it.paramType == ActionParameterType.PARAMDATA || - it.paramType == ActionParameterType.UPDATE_DATE_ON_EDIT) && it.value.isNotEmpty() - } - ?.associate { it.key to it.value } ?: emptyMap() + val paramsMap: Map = + paramsList + ?.asSequence() + ?.filter { + (it.paramType == ActionParameterType.PARAMDATA || + it.paramType == ActionParameterType.UPDATE_DATE_ON_EDIT) && it.value.isNotEmpty() + } + ?.associate { it.key to it.value } ?: emptyMap() - val profileConfiguration = retrieveProfileConfiguration(profileId, paramsMap) - val resourceConfig = fhirResourceConfig ?: profileConfiguration.fhirResource - val baseResourceConfig = resourceConfig.baseResource + val profileConfiguration = retrieveProfileConfiguration(profileId, paramsMap) + val resourceConfig = fhirResourceConfig ?: profileConfiguration.fhirResource + val baseResourceConfig = resourceConfig.baseResource - val baseResource: Resource = - fhirEngine.get(baseResourceConfig.resource, resourceId.extractLogicalIdUuid()) + val baseResource: Resource = + fhirEngine.get(baseResourceConfig.resource, resourceId.extractLogicalIdUuid()) - val configComputedRuleValues = profileConfiguration.configRules.configRulesComputedValues() + val configComputedRuleValues = profileConfiguration.configRules.configRulesComputedValues() - val retrievedRelatedResources = - retrieveRelatedResources( - resources = listOf(baseResource), - relatedResourcesConfigs = resourceConfig.relatedResources, - relatedResourceWrapper = RelatedResourceWrapper(), - configComputedRuleValues = configComputedRuleValues, - ) - - RepositoryResourceData( - resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, - resource = baseResource, - relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, - relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, - secondaryRepositoryResourceData = - profileConfiguration.secondaryResources.retrieveSecondaryRepositoryResourceData( - profileConfiguration.filterActiveResources, - ), + val retrievedRelatedResources = + retrieveRelatedResources( + resources = listOf(baseResource), + relatedResourcesConfigs = resourceConfig.relatedResources, + relatedResourceWrapper = RelatedResourceWrapper(), + configComputedRuleValues = configComputedRuleValues, ) - } + + return RepositoryResourceData( + resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name, + resource = baseResource, + relatedResourcesMap = retrievedRelatedResources.relatedResourceMap, + relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap, + secondaryRepositoryResourceData = + profileConfiguration.secondaryResources.retrieveSecondaryRepositoryResourceData( + profileConfiguration.filterActiveResources, + ), + ) } fun retrieveProfileConfiguration(profileId: String, paramsMap: Map) = diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/FhirExtractionTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/FhirExtractionTest.kt index 79cc9d7842..0b8d49b085 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/FhirExtractionTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/FhirExtractionTest.kt @@ -34,7 +34,6 @@ import javax.inject.Inject import junit.framework.Assert.assertEquals import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertTrue -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Encounter @@ -75,7 +74,6 @@ class FhirExtractionTest : RobolectricTest() { hiltRule.inject() structureMapUtilities = StructureMapUtilities(transformSupportServices.simpleWorkerContext) val workManager = mockk() - every { defaultRepository.dispatcherProvider.io() } returns Dispatchers.IO every { defaultRepository.fhirEngine } returns fhirEngine every { workManager.enqueue(any()) } returns mockk() } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt index d9b0ceba8e..55d918e9f1 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt @@ -98,7 +98,6 @@ import org.smartregister.fhircore.engine.domain.model.ResourceFilterExpression import org.smartregister.fhircore.engine.domain.model.RuleConfig import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.formatDate @@ -129,19 +128,16 @@ class DefaultRepositoryTest : RobolectricTest() { private val application = ApplicationProvider.getApplicationContext() private val configurationRegistry: ConfigurationRegistry = Faker.buildTestConfigurationRegistry() private val context = ApplicationProvider.getApplicationContext() - private lateinit var dispatcherProvider: DefaultDispatcherProvider private lateinit var sharedPreferenceHelper: SharedPreferencesHelper private lateinit var defaultRepository: DefaultRepository @Before fun setUp() { hiltRule.inject() - dispatcherProvider = DefaultDispatcherProvider() sharedPreferenceHelper = SharedPreferencesHelper(application, gson) defaultRepository = DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferenceHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -554,7 +550,6 @@ class DefaultRepositoryTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = mockk(), configService = mockk(), @@ -632,7 +627,6 @@ class DefaultRepositoryTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = mockk(), configService = mockk(), diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt index 1dc0027224..2da2b9fbfd 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt @@ -67,7 +67,6 @@ import org.smartregister.fhircore.engine.domain.model.SyncLocationState import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rule.CoroutineTestRule import org.smartregister.fhircore.engine.rulesengine.RulesFactory -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.encodeJson import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -99,8 +98,6 @@ class RegisterRepositoryTest : RobolectricTest() { @Inject lateinit var fhirPathDataExtractor: FhirPathDataExtractor - @Inject lateinit var dispatcherProvider: DispatcherProvider - @Inject lateinit var fhirEngine: FhirEngine @Inject lateinit var parser: IParser @@ -115,7 +112,6 @@ class RegisterRepositoryTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt index 589a5494c6..fec08a518b 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt @@ -113,7 +113,6 @@ import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rule.CoroutineTestRule -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.REFERENCE import org.smartregister.fhircore.engine.util.extension.SDF_YYYY_MM_DD import org.smartregister.fhircore.engine.util.extension.asReference @@ -147,8 +146,6 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { @Inject lateinit var fhirEngine: FhirEngine - @Inject lateinit var testDispatcher: DispatcherProvider - @Inject lateinit var configurationRegistry: ConfigurationRegistry private val context: Context = ApplicationProvider.getApplicationContext() @@ -171,7 +168,6 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { fun setup() { hiltRule.inject() structureMapUtilities = StructureMapUtilities(transformSupportServices.simpleWorkerContext) - every { defaultRepository.dispatcherProvider } returns testDispatcher every { defaultRepository.fhirEngine } returns fhirEngine coEvery { defaultRepository.create(anyBoolean(), any()) } returns listOf() diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt index e6ce1dfe6b..1490a719f6 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt @@ -95,7 +95,7 @@ class FhirResourceExpireWorkerTest : RobolectricTest() { period = Period().apply { end = DateTime().plusDays(-2).toDate() } } } - val serviceRequest = + private val serviceRequest = ServiceRequest().apply { id = UUID.randomUUID().toString() status = ServiceRequest.ServiceRequestStatus.COMPLETED @@ -112,7 +112,6 @@ class FhirResourceExpireWorkerTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index a50b491869..110da18a5b 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -25,7 +25,6 @@ import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject -import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.Measure @@ -36,7 +35,6 @@ import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.configuration.report.measure.ReportConfiguration import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -47,7 +45,6 @@ class MeasureReportRepository @Inject constructor( override val fhirEngine: FhirEngine, - override val dispatcherProvider: DispatcherProvider, override val sharedPreferencesHelper: SharedPreferencesHelper, override val configurationRegistry: ConfigurationRegistry, override val configService: ConfigService, @@ -60,7 +57,6 @@ constructor( ) : DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -91,31 +87,29 @@ constructor( ): List { val measureReport = mutableListOf() try { - withContext(dispatcherProvider.io()) { - if (subjects.isNotEmpty()) { - subjects - .map { - runMeasureReport( - measureUrl = measureUrl, - reportType = MeasureReportViewModel.SUBJECT, - startDateFormatted = startDateFormatted, - endDateFormatted = endDateFormatted, - subject = it, - practitionerId = practitionerId, - ) - } - .forEach { subject -> measureReport.add(subject) } - } else { - runMeasureReport( + if (subjects.isNotEmpty()) { + subjects + .map { + runMeasureReport( measureUrl = measureUrl, - reportType = MeasureReportViewModel.POPULATION, + reportType = MeasureReportViewModel.SUBJECT, startDateFormatted = startDateFormatted, endDateFormatted = endDateFormatted, - subject = null, + subject = it, practitionerId = practitionerId, ) - .also { measureReport.add(it) } - } + } + .forEach { subject -> measureReport.add(subject) } + } else { + runMeasureReport( + measureUrl = measureUrl, + reportType = MeasureReportViewModel.POPULATION, + startDateFormatted = startDateFormatted, + endDateFormatted = endDateFormatted, + subject = null, + practitionerId = practitionerId, + ) + .also { measureReport.add(it) } } measureReport.forEach { report -> diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt index 9c4d8d056f..af86768fbc 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModel.kt @@ -40,7 +40,6 @@ import org.smartregister.fhircore.engine.domain.model.ActionParameterType import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData import org.smartregister.fhircore.engine.domain.model.SnackBarMessageConfig import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferenceKey import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid @@ -54,7 +53,6 @@ class GeoWidgetLauncherViewModel @Inject constructor( val defaultRepository: DefaultRepository, - val dispatcherProvider: DispatcherProvider, val sharedPreferencesHelper: SharedPreferencesHelper, val resourceDataRulesExecutor: ResourceDataRulesExecutor, val configurationRegistry: ConfigurationRegistry, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/DataMigrationTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/DataMigrationTest.kt index 1aafa14d4f..7586d323b6 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/DataMigrationTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/DataMigrationTest.kt @@ -77,6 +77,24 @@ class DataMigrationTest : RobolectricTest() { dataMigration.migrate( migrationConfigs = listOf( + MigrationConfig( + resourceConfig = + FhirResourceConfig( + baseResource = ResourceConfig(resource = ResourceType.Patient), + ), + version = 7, + rules = + listOf( + RuleConfig(name = "value", actions = listOf("data.put('value', 'female')")), + ), + updateValues = + listOf( + UpdateValueConfig( + jsonPathExpression = "\$.gender", + computedValueKey = "value", + ), + ), + ), MigrationConfig( resourceConfig = FhirResourceConfig( @@ -103,9 +121,9 @@ class DataMigrationTest : RobolectricTest() { Assert.assertTrue(updatedPatient?.gender != patient.gender) Assert.assertEquals(Enumerations.AdministrativeGender.FEMALE, updatedPatient?.gender) - // Version updated to 2 + // Version updated to 7 (the maximum migration version) Assert.assertEquals( - 2, + 7, preferenceDataStore.read(PreferenceDataStore.MIGRATION_VERSION).first(), ) } @@ -179,9 +197,9 @@ class DataMigrationTest : RobolectricTest() { Assert.assertNotNull(updatedTask?.basedOn) Assert.assertEquals("CarePlan/${carePlan.logicalId}", updatedTask?.basedOnFirstRep?.reference) - // Version updated to 2 + // Version updated to 1 Assert.assertEquals( - 2, + 1, preferenceDataStore.read(PreferenceDataStore.MIGRATION_VERSION).first(), ) } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt index e16149d5c8..b24e6d90ad 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt @@ -49,7 +49,6 @@ import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor import org.smartregister.fhircore.engine.rulesengine.RulesFactory import org.smartregister.fhircore.engine.rulesengine.services.LocationService -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import org.smartregister.fhircore.quest.app.fakes.Faker @@ -108,7 +107,6 @@ class MeasureReportPagingSourceTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = fhirEngine, - dispatcherProvider = DefaultDispatcherProvider(), sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt index 5dc3b41746..b0c43d33f4 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt @@ -49,7 +49,6 @@ import org.smartregister.fhircore.engine.data.local.register.RegisterRepository import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor import org.smartregister.fhircore.engine.rulesengine.RulesFactory import org.smartregister.fhircore.engine.rulesengine.services.LocationService -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.SDF_YYYY_MM_DD import org.smartregister.fhircore.engine.util.extension.firstDayOfMonth @@ -114,7 +113,6 @@ class MeasureReportRepositoryTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = fhirEngine, - dispatcherProvider = DefaultDispatcherProvider(), sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), @@ -128,7 +126,6 @@ class MeasureReportRepositoryTest : RobolectricTest() { measureReportRepository = MeasureReportRepository( fhirEngine = fhirEngine, - dispatcherProvider = DefaultDispatcherProvider(), sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt index e2a47a82cf..8b56a2dfa3 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt @@ -49,7 +49,6 @@ import org.smartregister.fhircore.engine.domain.model.FhirResourceConfig import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.domain.model.SnackBarMessageConfig import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.geowidget.model.GeoJsonFeature import org.smartregister.fhircore.quest.app.fakes.Faker @@ -63,8 +62,6 @@ class GeoWidgetLauncherViewModelTest : RobolectricTest() { @Inject lateinit var defaultRepository: DefaultRepository - @Inject lateinit var dispatcherProvider: DefaultDispatcherProvider - @Inject lateinit var sharedPreferencesHelper: SharedPreferencesHelper @Inject lateinit var resourceDataRulesExecutor: ResourceDataRulesExecutor @@ -102,7 +99,6 @@ class GeoWidgetLauncherViewModelTest : RobolectricTest() { viewModel = GeoWidgetLauncherViewModel( defaultRepository = defaultRepository, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, resourceDataRulesExecutor = resourceDataRulesExecutor, configurationRegistry = configurationRegistry, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt index 9c63ea2390..4990d13671 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt @@ -54,7 +54,6 @@ import org.smartregister.fhircore.engine.domain.model.OverflowMenuItemConfig import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData import org.smartregister.fhircore.engine.domain.model.ResourceData import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor -import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.BLACK_COLOR_HEX_CODE import org.smartregister.fhircore.engine.util.extension.getActivity @@ -99,7 +98,6 @@ class ProfileViewModelTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = mockk(), - dispatcherProvider = DefaultDispatcherProvider(), sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt index e963a2fdf2..2c80d74397 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivityTest.kt @@ -68,7 +68,6 @@ 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.RuleConfig -import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.decodeResourceFromString import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.quest.R @@ -89,8 +88,6 @@ class QuestionnaireActivityTest : RobolectricTest() { private lateinit var questionnaireActivityController: ActivityController private lateinit var questionnaireActivity: QuestionnaireActivity - @Inject lateinit var testDispatcherProvider: DispatcherProvider - @BindValue lateinit var defaultRepository: DefaultRepository @BindValue @@ -104,7 +101,6 @@ class QuestionnaireActivityTest : RobolectricTest() { } defaultRepository = mockk(relaxUnitFun = true) { - every { dispatcherProvider } returns testDispatcherProvider every { fhirEngine } returns spyk(this@QuestionnaireActivityTest.fhirEngine) } questionnaireConfig = diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 88f62221e5..103341e4a2 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -175,7 +175,6 @@ class QuestionnaireViewModelTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -198,7 +197,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { spyk( QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), From 6d35db9dd6f3f3fe8801c7d72880c7a0d7c9096b Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 16 Aug 2024 12:40:25 +0300 Subject: [PATCH 071/110] =?UTF-8?q?Fix=20unit=20test=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fhircore/geowidget/screens/GeoWidgetViewModelTest.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt index b84dcd5928..baaf1e49cd 100644 --- a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt +++ b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt @@ -48,7 +48,6 @@ import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor import org.smartregister.fhircore.geowidget.model.GeoJsonFeature import org.smartregister.fhircore.geowidget.model.Geometry import org.smartregister.fhircore.geowidget.model.ServicePointType -import org.smartregister.fhircore.geowidget.rule.CoroutineTestRule @RunWith(RobolectricTestRunner::class) @Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class) @@ -59,8 +58,6 @@ class GeoWidgetViewModelTest { @get:Rule(order = 1) var instantTaskExecutorRule = InstantTaskExecutorRule() - @get:Rule(order = 2) var coroutinesTestRule = CoroutineTestRule() - @Inject lateinit var configService: ConfigService private lateinit var configurationRegistry: ConfigurationRegistry @@ -93,7 +90,6 @@ class GeoWidgetViewModelTest { spyk( DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = coroutinesTestRule.testDispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -103,7 +99,7 @@ class GeoWidgetViewModelTest { context = ApplicationProvider.getApplicationContext(), ), ) - geoWidgetViewModel = spyk(GeoWidgetViewModel(coroutinesTestRule.testDispatcherProvider)) + geoWidgetViewModel = spyk(GeoWidgetViewModel(dispatcherProvider)) coEvery { defaultRepository.create(any()) } returns emptyList() } From 21420ae7872c06ceb42411e0ddba8221c383f038 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 19 Aug 2024 19:58:44 +0300 Subject: [PATCH 072/110] Refactor Knowledge Manager Resources Persistance --- .../configuration/ConfigurationRegistry.kt | 12 ++++++--- .../fhircore/engine/di/CoreModule.kt | 25 ++++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index 6dc039191f..5aebbe77ca 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -627,9 +627,14 @@ constructor( resource.idElement.idPart } - return File(context.filesDir, "$fileName.json").apply { - writeText(jsonParser.encodeResourceToString(resource)) - } + return File( + context.filesDir, + "$KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER/${resource.resourceType}/$fileName.json", + ) + .apply { + this.parentFile?.mkdirs() + writeText(jsonParser.encodeResourceToString(resource)) + } } /** @@ -821,6 +826,7 @@ constructor( const val PAGINATION_NEXT = "next" const val RESOURCES_PATH = "resources/" const val SYNC_LOCATION_IDS = "_syncLocations" + const val KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER = "km" /** * The list of resources whose types can be synced down as part of the Composition configs. diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/di/CoreModule.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/di/CoreModule.kt index a3a1eb6d01..7ffedd809b 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/di/CoreModule.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/di/CoreModule.kt @@ -27,10 +27,14 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import java.io.File +import java.io.FileInputStream import javax.inject.Singleton import org.hl7.fhir.r4.context.SimpleWorkerContext import org.hl7.fhir.r4.model.Parameters +import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.utils.FHIRPathEngine +import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.util.helper.TransformSupportServices @InstallIn(SingletonComponent::class) @@ -39,10 +43,29 @@ class CoreModule { @Singleton @Provides - fun provideWorkerContextProvider(): SimpleWorkerContext = + fun provideWorkerContextProvider(@ApplicationContext context: Context): SimpleWorkerContext = SimpleWorkerContext().apply { setExpansionProfile(Parameters()) isCanRunWithoutTerminology = true + context.filesDir + .resolve(ConfigurationRegistry.KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER) + .list() + ?.forEach { resourceFolder -> + context.filesDir + .resolve("${ConfigurationRegistry.KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER}/$resourceFolder") + .list() + ?.forEach { file -> + cacheResource( + FhirContext.forR4Cached() + .newJsonParser() + .parseResource( + FileInputStream( + File(context.filesDir.resolve("km/$resourceFolder/$file").toString()), + ), + ) as Resource, + ) + } + } } @Singleton From 692062026bf079f573565ac0b2d3e879bddfab2e Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 20 Aug 2024 08:43:56 +0300 Subject: [PATCH 073/110] Refactor CQL Content tests --- .../fhircore/quest/CqlContentTest.kt | 183 +++++++++--------- 1 file changed, 94 insertions(+), 89 deletions(-) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index 7705221fad..bad1f79522 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -23,8 +23,10 @@ import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import java.io.File import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.Condition import org.hl7.fhir.r4.model.Library import org.hl7.fhir.r4.model.Parameters import org.hl7.fhir.r4.model.Resource @@ -40,9 +42,9 @@ import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.valueToString import org.smartregister.fhircore.quest.robolectric.RobolectricTest import org.smartregister.fhircore.quest.sdk.CqlBuilder -import org.smartregister.fhircore.quest.sdk.runBlockingOnWorkerThread @HiltAndroidTest +@OptIn(ExperimentalCoroutinesApi::class) class CqlContentTest : RobolectricTest() { @get:Rule var hiltRule = HiltAndroidRule(this) @@ -61,112 +63,115 @@ class CqlContentTest : RobolectricTest() { } @Test - fun runCqlLibraryTestForPqMedication() = runBlockingOnWorkerThread { - val resourceDir = "cql/pq-medication" - val cql = "$resourceDir/cql.txt".readFile() - - val cqlLibrary = buildCqlLibrary(cql) - - val dataBundle = - loadTestResultsSampleData().apply { - // output of test results cql is also added to input of this cql - "cql/test-results/sample" - .readDir() - .map { it.parseSampleResource() as Resource } - .forEach { addEntry().apply { resource = it } } - } + fun runCqlLibraryTestForPqMedication() = + runTest(context = UnconfinedTestDispatcher()) { + val resourceDir = "cql/pq-medication" + val cql = "$resourceDir/cql.txt".readFile() + + val cqlLibrary = buildCqlLibrary(cql) + + val dataBundle = + loadTestResultsSampleData().apply { + // output of test results cql is also added to input of this cql + "cql/test-results/sample" + .readDir() + .map { it.parseSampleResource() as Resource } + .forEach { addEntry().apply { resource = it } } + } - createTestData(dataBundle, cqlLibrary) + createTestData(dataBundle, cqlLibrary) - val result = - fhirOperator.evaluateLibrary( - cqlLibrary.url, - dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, - null, - ) as Parameters + val result = + fhirOperator.evaluateLibrary( + cqlLibrary.url, + dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, + null, + ) as Parameters - printResult(result) + printResult(result) - assertOutput( - "$resourceDir/output_medication_request.json", - result, - ResourceType.MedicationRequest, - ) - } + assertOutput( + "$resourceDir/output_medication_request.json", + result, + ResourceType.MedicationRequest, + ) + } @Test - fun runCqlLibraryTestForTestResults() = runBlockingOnWorkerThread { - val resourceDir = "cql/test-results" - val cql = "$resourceDir/cql.txt".readFile() + fun runCqlLibraryTestForTestResults() = + runTest(context = UnconfinedTestDispatcher()) { + val resourceDir = "cql/test-results" + val cql = "$resourceDir/cql.txt".readFile() - val cqlLibrary = buildCqlLibrary(cql) + val cqlLibrary = buildCqlLibrary(cql) - val dataBundle = loadTestResultsSampleData() + val dataBundle = loadTestResultsSampleData() - createTestData(dataBundle, cqlLibrary) + createTestData(dataBundle, cqlLibrary) - val result = - fhirOperator.evaluateLibrary( - cqlLibrary.url, - dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, - null, - null, - null, - ) as Parameters + val result = + fhirOperator.evaluateLibrary( + cqlLibrary.url, + dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, + null, + null, + null, + ) as Parameters - printResult(result) + printResult(result) - assertOutput("$resourceDir/sample/output_condition.json", result, ResourceType.Condition) - assertOutput( - "$resourceDir/sample/output_service_request.json", - result, - ResourceType.ServiceRequest, - ) - assertOutput( - "$resourceDir/sample/output_diagnostic_report.json", - result, - ResourceType.DiagnosticReport, - ) - } + assertOutput("$resourceDir/sample/output_condition.json", result, ResourceType.Condition) + assertOutput( + "$resourceDir/sample/output_service_request.json", + result, + ResourceType.ServiceRequest, + ) + assertOutput( + "$resourceDir/sample/output_diagnostic_report.json", + result, + ResourceType.DiagnosticReport, + ) + } @Test - fun runCqlLibraryTestForControlTest() = runBlockingOnWorkerThread { - val resourceDir = "cql/control-test" - val cql = "$resourceDir/cql.txt".readFile() - - val cqlLibrary = buildCqlLibrary(cql) - - val dataBundle = - loadTestResultsSampleData().apply { - addEntry().apply { - // questionnaire-response of test results is input of this cql - resource = - "test-results-questionnaire/questionnaire-response.json".parseSampleResourceFromFile() - as Resource + fun runCqlLibraryTestForControlTest() = + runTest(context = UnconfinedTestDispatcher()) { + val resourceDir = "cql/control-test" + val cql = "$resourceDir/cql.txt".readFile() + + val cqlLibrary = buildCqlLibrary(cql) + + val dataBundle = + loadTestResultsSampleData().apply { + addEntry().apply { + // questionnaire-response of test results is input of this cql + resource = + "test-results-questionnaire/questionnaire-response.json".parseSampleResourceFromFile() + as Resource + } } - } - createTestData(dataBundle, cqlLibrary) + createTestData(dataBundle, cqlLibrary) - val result = - fhirOperator.evaluateLibrary( - cqlLibrary.url, - dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, - null, - ) as Parameters + val result = + fhirOperator.evaluateLibrary( + cqlLibrary.url, + dataBundle.entry.find { it.resource.resourceType == ResourceType.Patient }!!.resource.id, + null, + ) as Parameters - printResult(result) + printResult(result) - Assert.assertTrue( - result.getParameterValues("OUTPUT").first().valueToString() == "Correct Result", - ) - Assert.assertEquals( - result.getParameterValues("OUTPUT").elementAt(1).valueToString(), - "\nDetails:\n" + - "Value (3.0) is in Normal G6PD Range 0-3\n" + - "Value (11.0) is in Normal Haemoglobin Range 8-12", - ) - } + Assert.assertTrue( + result.getParameterValues("OUTPUT").first().valueToString() == "Correct Result", + ) + Assert.assertEquals( + result.getParameterValues("OUTPUT").elementAt(1).valueToString(), + "\nDetails:\n" + + "Value (3.0) is in Normal G6PD Range 0-3\n" + + "Value (11.0) is in Normal Haemoglobin Range 8-12", + ) + } private fun buildCqlLibrary(cql: String): Library { val cqlCompiler = CqlBuilder.compile(cql) From fdc9097df079e98b777c7b54c7e46fa85d63f43b Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 20 Aug 2024 08:45:11 +0300 Subject: [PATCH 074/110] =?UTF-8?q?Fix=20AppSettingModel=20unit=20tests=20?= =?UTF-8?q?=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/appsetting/AppSettingViewModel.kt | 5 ++- .../ui/appsetting/AppSettingViewModelTest.kt | 32 +++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt index 03e1cd12fb..7e1f84e86d 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModel.kt @@ -24,6 +24,7 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import java.net.UnknownHostException import javax.inject.Inject +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.RequestBody.Companion.toRequestBody @@ -101,8 +102,10 @@ constructor( } } + private val exceptionHandler = CoroutineExceptionHandler { _, exception -> Timber.e(exception) } + private fun fetchRemoteConfigurations(appId: String?, context: Context) { - viewModelScope.launch { + viewModelScope.launch(exceptionHandler) { try { showProgressBar.postValue(true) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModelTest.kt index a8889e10e3..ece7d30f2e 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/appsetting/AppSettingViewModelTest.kt @@ -34,9 +34,9 @@ import io.mockk.verify import java.net.UnknownHostException import java.nio.charset.StandardCharsets import javax.inject.Inject -import kotlin.test.assertEquals import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody @@ -131,23 +131,35 @@ class AppSettingViewModelTest : RobolectricTest() { @Test fun testFetchConfigurations() = - runTest(timeout = 90.seconds) { - fhirEngine.create(Composition().apply { id = "sampleComposition" }) + runTest(timeout = 90.seconds, context = UnconfinedTestDispatcher()) { val appId = "test_app_id" appSettingViewModel.onApplicationIdChanged(appId) - coEvery { fhirResourceDataSource.getResource(any()) } returns - Bundle().apply { - addEntry().resource = - Composition().apply { - addSection().apply { this.focus = Reference().apply { reference = "Binary/123" } } - } + coEvery { + appSettingViewModel.configurationRegistry.fetchRemoteCompositionByAppId(any()) + } returns + Composition().apply { + addSection().apply { this.focus = Reference().apply { reference = "Binary/123" } } } + + coEvery { + appSettingViewModel.configurationRegistry.loadConfigurations(any(), any(), any()) + } just runs + + coEvery { appSettingViewModel.fhirResourceDataSource.post(any(), any()) } returns Bundle() + coEvery { appSettingViewModel.defaultRepository.createRemote(any(), any()) } just runs + coEvery { + appSettingViewModel.configurationRegistry.fetchRemoteImplementationGuideByAppId( + appId, + QuestBuildConfig.VERSION_CODE, + ) + } returns null + appSettingViewModel.fetchConfigurations(context) - coVerify { fhirResourceDataSource.getResource(any()) } + coVerify { appSettingViewModel.configurationRegistry.fetchRemoteCompositionByAppId(any()) } coVerify { appSettingViewModel.defaultRepository.createRemote(any(), any()) } } From ed1dee78df934e464f3ec1a8a7debd98967ff7e6 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 20 Aug 2024 11:36:29 +0300 Subject: [PATCH 075/110] Refactor Cancel previous worflow to use native commands --- .github/workflows/ci.yml | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1c16c67ae..3dfa781720 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,10 @@ on: merge_group: branches: [ main ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: FHIRCORE_USERNAME: ${{ secrets.FHIRCORE_USERNAME }} FHIRCORE_ACCESS_TOKEN: ${{ secrets.FHIRCORE_ACCESS_TOKEN }} @@ -22,12 +26,8 @@ jobs: strategy: matrix: api-level: [30] + steps: - - name: Cancel Previous workflow runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - name: Checkout 🛎️ uses: actions/checkout@v4 @@ -115,12 +115,8 @@ jobs: strategy: matrix: api-level: [30] + steps: - - name: Cancel Previous workflow runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - name: Checkout 🛎️ uses: actions/checkout@v4 @@ -207,11 +203,7 @@ jobs: strategy: matrix: api-level: [30] - steps: - - name: Cancel Previous workflow runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} + - name: Checkout 🛎️ uses: actions/checkout@v4 @@ -317,4 +309,4 @@ jobs: - name: Upload Quest module test coverage report to Codecov if: matrix.api-level == 30 # Only upload coverage on API level 30 working-directory: android - run: bash <(curl -s https://codecov.io/bash) -F quest -f "quest/build/reports/jacoco/fhircoreJacocoReport/fhircoreJacocoReport.xml" \ No newline at end of file + run: bash <(curl -s https://codecov.io/bash) -F quest -f "quest/build/reports/jacoco/fhircoreJacocoReport/fhircoreJacocoReport.xml" From 35f22b4f2b2401a08edef6fde1e1d77797757495 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 20 Aug 2024 11:37:55 +0300 Subject: [PATCH 076/110] Upgrade CI API level to 34 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3dfa781720..e66e2ab4c2 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - api-level: [30] + api-level: [34] steps: - name: Checkout 🛎️ @@ -114,7 +114,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - api-level: [30] + api-level: [34] steps: - name: Checkout 🛎️ @@ -194,7 +194,7 @@ jobs: path: android/geowidget/build/reports - name: Upload Geowidget module test coverage report to Codecov - if: matrix.api-level == 30 # Only upload coverage on API level 30 + if: matrix.api-level == 34 # Only upload coverage on API level 34 working-directory: android run: bash <(curl -s https://codecov.io/bash) -F geowidget -f "geowidget/build/reports/jacoco/fhircoreJacocoReport/fhircoreJacocoReport.xml" @@ -202,7 +202,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - api-level: [30] + api-level: [34] - name: Checkout 🛎️ uses: actions/checkout@v4 From 9d83a1d31672d0e676b387e032ab781021521484 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 21 Aug 2024 16:31:01 +0300 Subject: [PATCH 077/110] Clean up Translations --- android/quest/src/main/res/values-fr/strings.xml | 4 ++-- android/quest/src/main/res/values-in/strings.xml | 6 ------ android/quest/src/main/res/values-sw/strings.xml | 1 + android/quest/src/main/res/values/strings.xml | 3 ++- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/android/quest/src/main/res/values-fr/strings.xml b/android/quest/src/main/res/values-fr/strings.xml index 4d45d4ce5e..ae4d01024d 100644 --- a/android/quest/src/main/res/values-fr/strings.xml +++ b/android/quest/src/main/res/values-fr/strings.xml @@ -99,6 +99,7 @@ Questionnaire introuvable, synchroniser tous les questionnaires pour régler ce problème Pas de visites Réponse du questionnaire invalide + Version Type de sujet manquant dans le questionnaire. Fournir Questionnaire.subjectType pour résoudre le problème. QuestionnaireConfig est requis mais manquant Erreur dans le remplissage de certains champs du questionnaire. Réponse au questionnaire non valide. @@ -126,7 +127,7 @@ Modifier Revoir les réponses Revoir - \\@android:string/cancel + Annuler Erreurs trouvées @@ -252,7 +253,6 @@ Optionnel Requis Requis\n - \u0020\u002a diff --git a/android/quest/src/main/res/values-in/strings.xml b/android/quest/src/main/res/values-in/strings.xml index 1e5339a53c..e77b34ba80 100644 --- a/android/quest/src/main/res/values-in/strings.xml +++ b/android/quest/src/main/res/values-in/strings.xml @@ -13,8 +13,6 @@ Tidak ada hasil tes ditemukan HASIL TES Tes terakhir - %1$s - 2.4 km - # Tugas Keluarga Kunjungan rutin @@ -88,11 +86,8 @@ Luaran Kehamilan Register - %1$s (%2$s) Dijadwalkan pada %1$s - GeoWidget Fragment Destination - Gagal mengekstraksi resources untuk %1$s StructureMap untuk Questionnaire tidak ada, QuestionnaireResponse disimpan Resources berhasil diekstraksi untuk %1$s @@ -101,7 +96,6 @@ Questionnaire tidak ditemukan. Sinkronkan semua kuesioner untuk memperbaiki Tidak ada kunjungan Respons kuesioner tidak valid - https://smartregister.org/app-version Versi aplikasi Jenis subjek pada kuesioner tidak ada. Berikan Questionnaire.subjectType untuk diselesaikan. QuestionnaireConfig diperlukan tetapi tidak ada. diff --git a/android/quest/src/main/res/values-sw/strings.xml b/android/quest/src/main/res/values-sw/strings.xml index d81539309a..4d8a8be8f7 100644 --- a/android/quest/src/main/res/values-sw/strings.xml +++ b/android/quest/src/main/res/values-sw/strings.xml @@ -73,4 +73,5 @@ Ramani ya Muundo Haipo kwa Hojaji , HojajiJibu limehifadhiwa Imetoa rasilimali za %1$s Imeshindwa kutoa nyenzo za dodoso %1$s + Futa yote diff --git a/android/quest/src/main/res/values/strings.xml b/android/quest/src/main/res/values/strings.xml index ac715710f0..9a21e34727 100644 --- a/android/quest/src/main/res/values/strings.xml +++ b/android/quest/src/main/res/values/strings.xml @@ -107,7 +107,7 @@ Validation on extracted resources failed. Please check the logs An error occurred generating CarePlan. Please check the logs https://smartregister.org/app-version - Application Version + Application Version Missing subject type on questionnaire. Provide Questionnaire.subjectType to resolve. QuestionnaireConfig is required but missing. Error populating some questionnaire fields. Invalid QuestionnaireResponse. @@ -132,4 +132,5 @@ Scan QR Code Place your camera over the entire QR Code to start scanning Failed to get GPS location + \\u0020\\u002a From 8aa57402d3c6b60a5391408a4f8ec1d64ce715c3 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 28 Aug 2024 11:14:41 +0300 Subject: [PATCH 078/110] Fix measure reporting --- .../report/measure/MeasureReportViewModel.kt | 18 +++++++++--------- .../measure/MeasureReportViewModelTest.kt | 8 ++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt index b44219ffd4..a5445c7ca8 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt @@ -328,16 +328,16 @@ constructor( ) { withContext(dispatcherProvider.io()) { fhirEngine.loadCqlLibraryBundle(fhirOperator, config.url) - } - measureReportRepository.evaluatePopulationMeasure( - measureUrl = config.url, - startDateFormatted = startDateFormatted, - endDateFormatted = endDateFormatted, - subjects = subjects, - existing = existingValidReports, - practitionerId = practitionerId, - ) + measureReportRepository.evaluatePopulationMeasure( + measureUrl = config.url, + startDateFormatted = startDateFormatted, + endDateFormatted = endDateFormatted, + subjects = subjects, + existing = existingValidReports, + practitionerId = practitionerId, + ) + } } else { existingValidReports } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt index 886c4d3544..37e07977c8 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt @@ -52,6 +52,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.CodeableConcept import org.hl7.fhir.r4.model.Coding @@ -109,6 +110,9 @@ class MeasureReportViewModelTest : RobolectricTest() { @Inject lateinit var resourceDataRulesExecutor: ResourceDataRulesExecutor + @OptIn(ExperimentalCoroutinesApi::class) + private val unconfinedTestDispatcher = UnconfinedTestDispatcher() + @Inject lateinit var fhirEngine: FhirEngine private val measureReportRepository: MeasureReportRepository = mockk() private val fhirOperator: FhirOperator = mockk() @@ -302,9 +306,9 @@ class MeasureReportViewModelTest : RobolectricTest() { Assert.assertNotNull(sampleSubjectViewData.family, subjectViewData?.family) } - @Test() + @Test fun testEvaluateMeasureUtilizesPreviouslyGeneratedMeasureReportIfAvailable() = - runTest(timeout = 90.seconds) { + runTest(timeout = 90.seconds, context = unconfinedTestDispatcher) { val subject = Group().apply { id = "groupId" } val testMeasureReport = MeasureReport().apply { From 5074b5ab18e44d433fde15960cdeff37358f39da Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 28 Aug 2024 12:18:22 +0300 Subject: [PATCH 079/110] Fix Workflow Configuration --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e66e2ab4c2..3a259f1eb9 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -204,6 +204,7 @@ jobs: matrix: api-level: [34] + steps: - name: Checkout 🛎️ uses: actions/checkout@v4 From e603cdac1e4fda407679daf84b3d27df9c1611ef Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 28 Aug 2024 19:00:56 +0300 Subject: [PATCH 080/110] =?UTF-8?q?Fix=20QuestionnaireViewModel=20unit=20t?= =?UTF-8?q?ests=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../questionnaire/QuestionnaireViewModel.kt | 92 +++++++++---------- .../QuestionnaireViewModelTest.kt | 90 ++++++++++++------ 2 files changed, 107 insertions(+), 75 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 90c3747bf8..ae70ed686b 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -31,8 +31,8 @@ import com.google.android.fhir.datacapture.validation.NotValidated import com.google.android.fhir.datacapture.validation.QuestionnaireResponseValidator import com.google.android.fhir.datacapture.validation.Valid import com.google.android.fhir.db.ResourceNotFoundException +import com.google.android.fhir.knowledge.KnowledgeManager import com.google.android.fhir.search.Search -import com.google.android.fhir.search.filter.TokenParamFilterCriterion import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.lifecycle.HiltViewModel @@ -116,6 +116,7 @@ constructor( val fhirValidatorProvider: Provider, val fhirPathDataExtractor: FhirPathDataExtractor, val configurationRegistry: ConfigurationRegistry, + val knowledgeManager: KnowledgeManager, ) : ViewModel() { private val parser = FhirContext.forR4Cached().newJsonParser() @@ -780,58 +781,53 @@ constructor( bundle.addEntry(Bundle.BundleEntryComponent().setResource(basicResource)) } - val libraryFilters = - questionnaire.cqfLibraryUrls().map { - val apply: TokenParamFilterCriterion.() -> Unit = { value = of(it.extractLogicalIdUuid()) } - apply - } - - if (libraryFilters.isNotEmpty()) { - defaultRepository.fhirEngine - .search { - filter( - Resource.RES_ID, - *libraryFilters.toTypedArray(), + questionnaire.cqfLibraryUrls().forEach { libraryUrl -> + val librariesList = + withContext(dispatcherProvider.io()) { + knowledgeManager.loadResources( + ResourceType.Library.name, + libraryUrl, ) } - .forEach { librarySearchResult -> - val result: Parameters = - fhirOperator.evaluateLibrary( - librarySearchResult.resource.url, - subject.asReference().reference, - null, - bundle, - null, - ) as Parameters - - val resources = - result.parameter.mapNotNull { cqlResultParameterComponent -> - (cqlResultParameterComponent.value ?: cqlResultParameterComponent.resource)?.let { - resultParameterResource -> - if (BuildConfig.DEBUG) { - Timber.d( - "CQL :: Param found: ${cqlResultParameterComponent.name} with value: ${ - getStringRepresentation( - resultParameterResource, - ) - }", - ) - } - - if ( - cqlResultParameterComponent.name.equals(OUTPUT_PARAMETER_KEY) && - resultParameterResource.isResource - ) { - defaultRepository.create(true, resultParameterResource as Resource) - resultParameterResource - } else { - null - } + + librariesList.forEach { baseResource -> + val result: Parameters = + fhirOperator.evaluateLibrary( + (baseResource as Library).url, + subject.asReference().reference, + null, + bundle, + null, + ) as Parameters + + val resources = + result.parameter.mapNotNull { cqlResultParameterComponent -> + (cqlResultParameterComponent.value ?: cqlResultParameterComponent.resource)?.let { + resultParameterResource -> + if (BuildConfig.DEBUG) { + Timber.d( + "CQL :: Param found: ${cqlResultParameterComponent.name} with value: ${ + getStringRepresentation( + resultParameterResource, + ) + }", + ) + } + + if ( + cqlResultParameterComponent.name.equals(OUTPUT_PARAMETER_KEY) && + resultParameterResource.isResource + ) { + defaultRepository.create(true, resultParameterResource as Resource) + resultParameterResource + } else { + null } } + } - validateWithFhirValidator(*resources.toTypedArray()) - } + validateWithFhirValidator(*resources.toTypedArray()) + } } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 103341e4a2..6b02918404 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -26,6 +26,7 @@ import com.google.android.fhir.datacapture.extensions.logicalId 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.knowledge.KnowledgeManager import com.google.android.fhir.search.Search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.testing.HiltAndroidRule @@ -41,6 +42,7 @@ import io.mockk.slot import io.mockk.spyk import io.mockk.unmockkObject import io.mockk.verify +import java.io.File import java.util.Date import java.util.UUID import javax.inject.Inject @@ -49,8 +51,10 @@ import kotlin.test.assertEquals import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.hl7.fhir.r4.model.Address +import org.hl7.fhir.r4.model.Attachment import org.hl7.fhir.r4.model.Basic import org.hl7.fhir.r4.model.BooleanType import org.hl7.fhir.r4.model.Bundle @@ -66,6 +70,7 @@ import org.hl7.fhir.r4.model.Flag import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.IdType import org.hl7.fhir.r4.model.IntegerType +import org.hl7.fhir.r4.model.Library import org.hl7.fhir.r4.model.ListResource import org.hl7.fhir.r4.model.Location import org.hl7.fhir.r4.model.Observation @@ -103,6 +108,7 @@ import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.appendPractitionerInfo import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.decodeResourceFromString +import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.extension.find import org.smartregister.fhircore.engine.util.extension.isToday @@ -138,6 +144,8 @@ class QuestionnaireViewModelTest : RobolectricTest() { @Inject lateinit var parser: IParser + @Inject lateinit var knowledgeManager: KnowledgeManager + private lateinit var samplePatientRegisterQuestionnaire: Questionnaire private lateinit var questionnaireConfig: QuestionnaireConfig private lateinit var questionnaireViewModel: QuestionnaireViewModel @@ -206,6 +214,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirOperator = fhirOperator, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ), ) @@ -636,7 +645,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { val questionnaireViewModelInstance = QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), @@ -645,6 +654,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirValidatorProvider = fhirValidatorProvider, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ) val patientAgeLinkId = "patient-age" val newQuestionnaireConfig = @@ -1039,34 +1049,58 @@ class QuestionnaireViewModelTest : RobolectricTest() { } @Test - fun testExecuteCqlShouldInvokeRunCqlLibrary() = runTest { - val bundle = - Bundle().apply { addEntry(Bundle.BundleEntryComponent().apply { resource = patient }) } - - val questionnaire = - samplePatientRegisterQuestionnaire.copy().apply { - addExtension( - Extension().apply { - url = "https://sample.cqf-library.url" - setValue(StringType("http://smartreg.org/Library/123")) - }, - ) - } + fun testExecuteCqlShouldInvokeRunCqlLibrary() = + runTest(UnconfinedTestDispatcher()) { + val bundle = + Bundle().apply { addEntry(Bundle.BundleEntryComponent().apply { resource = patient }) } - coEvery { fhirOperator.evaluateLibrary(any(), any(), any(), any()) } returns Parameters() + val questionnaire = + samplePatientRegisterQuestionnaire.copy().apply { + addExtension( + Extension().apply { + url = "https://sample.cqf-library.url" + setValue(StringType("http://smartreg.org/Library/123")) + }, + ) + } - questionnaireViewModel.executeCql(patient, bundle, questionnaire) - fhirEngine.create(patient) + coEvery { fhirOperator.evaluateLibrary(any(), any(), any(), any()) } returns Parameters() + + val cqlLibrary = + Library().apply { + id = "Library/123" + url = "http://smartreg.org/Library/123" + name = "123" + version = "1.0.0" + status = Enumerations.PublicationStatus.ACTIVE + addContent( + Attachment().apply { + contentType = "text/cql" + data = "someCQL".toByteArray() + }, + ) + } - coVerify { - fhirOperator.evaluateLibrary( - "http://smartreg.org/Library/123", - patient.asReference().reference, - null, - expressions = setOf(), + knowledgeManager.install( + File.createTempFile(cqlLibrary.name, ".json").apply { + this.writeText(cqlLibrary.encodeResourceToString()) + }, ) + + fhirEngine.create(patient) + + questionnaireViewModel.executeCql(patient, bundle, questionnaire) + + coVerify { + fhirOperator.evaluateLibrary( + "http://smartreg.org/Library/123", + patient.asReference().reference, + null, + bundle, + null, + ) + } } - } @Test fun testGenerateCarePlan() = runTest { @@ -1258,7 +1292,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { encounterId = null, ) Assert.assertNotNull(latestQuestionnaireResponse) - Assert.assertEquals("qr1", latestQuestionnaireResponse?.id) + Assert.assertEquals("QuestionnaireResponse/qr1", latestQuestionnaireResponse?.id) } @Test @@ -1792,7 +1826,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { val questionnaireViewModelInstance = QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), @@ -1801,6 +1835,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirValidatorProvider = fhirValidatorProvider, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ) val questionnaireWithDefaultDate = Questionnaire().apply { @@ -1929,7 +1964,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { val questionnaireViewModelInstance = QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), @@ -1938,6 +1973,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirValidatorProvider = fhirValidatorProvider, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ) val questionnaireConfig1 = questionnaireConfig.copy( From 25df56e6b84ba17e2369fd5bb1e9a74317854818 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Thu, 29 Aug 2024 20:10:39 +0300 Subject: [PATCH 081/110] Clean up WorkManager after running unit tests --- .../fhircore/quest/robolectric/RobolectricTest.kt | 2 +- .../fhircore/quest/robolectric/WorkManagerRule.kt | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt index 7ef8fa6368..0698d2978b 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/RobolectricTest.kt @@ -100,7 +100,7 @@ abstract class RobolectricTest { fun File.parseSampleResource(): IBaseResource = sanitizeSampleResourceContent(this.readText()) - fun sanitizeSampleResourceContent(content: String): IBaseResource = + private fun sanitizeSampleResourceContent(content: String): IBaseResource = content .replace("#TODAY", Date().formatDate(SDF_YYYY_MM_DD)) .replace("#NOW", DateTimeType.now().valueAsString) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/WorkManagerRule.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/WorkManagerRule.kt index 9239735acb..515eb20688 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/WorkManagerRule.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/robolectric/WorkManagerRule.kt @@ -17,8 +17,10 @@ package org.smartregister.fhircore.quest.robolectric import android.util.Log +import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry import androidx.work.Configuration +import androidx.work.WorkManager import androidx.work.testing.SynchronousExecutor import androidx.work.testing.WorkManagerTestInitHelper import org.junit.rules.TestRule @@ -37,7 +39,11 @@ class WorkManagerRule : TestRule { .setExecutor(SynchronousExecutor()) .build() WorkManagerTestInitHelper.initializeTestWorkManager(context, config) - base.evaluate() + try { + base.evaluate() + } finally { + WorkManager.getInstance(ApplicationProvider.getApplicationContext()).cancelAllWork() + } } } } From 9b9f922cb2f56e5416671a427eee89c42537a510 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Thu, 29 Aug 2024 21:22:12 +0300 Subject: [PATCH 082/110] Remove skyscreamer test dependency --- android/quest/build.gradle.kts | 1 - .../java/org/smartregister/fhircore/quest/CqlContentTest.kt | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/android/quest/build.gradle.kts b/android/quest/build.gradle.kts index a4ffbeeab1..e4dd623b4c 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -457,7 +457,6 @@ dependencies { testImplementation(libs.navigation.testing) testImplementation(libs.kotlin.test) testImplementation(libs.work.testing) - testImplementation("org.skyscreamer:jsonassert:1.5.3") // To run only on debug builds debugImplementation(libs.ui.test.manifest) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index bad1f79522..012d5faccb 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -35,8 +35,6 @@ import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.encodeResourceToString import org.smartregister.fhircore.engine.util.extension.valueToString @@ -243,6 +241,6 @@ class CqlContentTest : RobolectricTest() { println(cqlResultStr) println(expectedResource) - JSONAssert.assertEquals(expectedResource, cqlResultStr, JSONCompareMode.STRICT) + Assert.assertEquals(expectedResource, cqlResultStr) } } From 2cfe37c72d1a1b9a083e5c0f3b8734797325c41f Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Thu, 29 Aug 2024 20:14:09 +0300 Subject: [PATCH 083/110] =?UTF-8?q?Fix=20build=20=F0=9F=92=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/measure/MeasureReportViewModel.kt | 155 +++++++++--------- .../measure/MeasureReportViewModelTest.kt | 17 +- 2 files changed, 86 insertions(+), 86 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt index a5445c7ca8..fe1769d270 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt @@ -33,12 +33,12 @@ import com.google.android.material.datepicker.MaterialDatePicker import dagger.hilt.android.lifecycle.HiltViewModel import java.util.* import javax.inject.Inject +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.MeasureReport import org.hl7.fhir.r4.model.Observation @@ -170,7 +170,9 @@ constructor( ) } refreshData() - event.practitionerId?.let { evaluateMeasure(event.navController, practitionerId = it) } + event.practitionerId?.let { + viewModelScope.launch { evaluateMeasure(event.navController, practitionerId = it) } + } } is MeasureReportEvent.OnDateSelected -> { if (selectedDate != null) { @@ -262,8 +264,10 @@ constructor( return subjectData.value } + private val exceptionHandler = CoroutineExceptionHandler { _, throwable -> println(throwable) } + // TODO: Enhancement - use FhirPathEngine evaluator for data extraction - fun evaluateMeasure(navController: NavController, practitionerId: String? = null) { + suspend fun evaluateMeasure(navController: NavController, practitionerId: String? = null) { // Run evaluate measure only for existing report if (reportConfigurations.isNotEmpty()) { // Retrieve and parse dates to (2020-11-16) @@ -277,91 +281,86 @@ constructor( .parseDate(SDF_D_MMM_YYYY_WITH_COMA) ?.formatDate(SDF_YYYY_MM_DD)!! - viewModelScope.launch { - kotlin - .runCatching { - // Show Progress indicator while evaluating measure - toggleProgressIndicatorVisibility(true) - val result = - reportConfigurations.flatMap { config -> - val subjects = mutableListOf() - subjects.addAll(measureReportRepository.fetchSubjects(config)) - - // If a practitioner Id is available, add it to the list of subjects - if (practitionerId?.isNotBlank() == true && subjects.isEmpty()) { - subjects.add("${Practitioner().resourceType.name}/$practitionerId") - } + try { + // Show Progress indicator while evaluating measure + toggleProgressIndicatorVisibility(true) + val result = + reportConfigurations.flatMap { config -> + val subjects = mutableListOf() + subjects.addAll(measureReportRepository.fetchSubjects(config)) + + // If a practitioner Id is available, add it to the list of subjects + if (practitionerId?.isNotBlank() == true && subjects.isEmpty()) { + subjects.add("${Practitioner().resourceType.name}/$practitionerId") + } - val existingReports = - fhirEngine.retrievePreviouslyGeneratedMeasureReports( - startDateFormatted = startDateFormatted, - endDateFormatted = endDateFormatted, - measureUrl = config.url, - subjects = listOf(), - ) + val existingReports = + fhirEngine.retrievePreviouslyGeneratedMeasureReports( + startDateFormatted = startDateFormatted, + endDateFormatted = endDateFormatted, + measureUrl = config.url, + subjects = listOf(), + ) + + val existingValidReports = mutableListOf() - val existingValidReports = mutableListOf() - - existingReports - .groupBy { it.subject.reference } - .forEach { entry -> - if ( - entry.value.size > 1 && - entry.value.distinctBy { it.measure }.size > 1 && - entry.value.distinctBy { it.type }.size > 1 - ) { - return@forEach - } else { - existingValidReports.addAll(entry.value) - } - } - - // if report is of current month or does not exist generate a new one and replace - // existing + existingReports + .groupBy { it.subject.reference } + .forEach { entry -> if ( - endDateFormatted - .parseDate(SDF_YYYY_MM_DD)!! - .formatDate(SDF_YYYY_MMM) - .contentEquals(Date().formatDate(SDF_YYYY_MMM)) || - existingValidReports.isEmpty() || - existingValidReports.size != subjects.size + entry.value.size > 1 && + entry.value.distinctBy { it.measure }.size > 1 && + entry.value.distinctBy { it.type }.size > 1 ) { - withContext(dispatcherProvider.io()) { - fhirEngine.loadCqlLibraryBundle(fhirOperator, config.url) - - measureReportRepository.evaluatePopulationMeasure( - measureUrl = config.url, - startDateFormatted = startDateFormatted, - endDateFormatted = endDateFormatted, - subjects = subjects, - existing = existingValidReports, - practitionerId = practitionerId, - ) - } + return@forEach } else { - existingValidReports + existingValidReports.addAll(entry.value) } } - val measureReportPopulationResultList = - formatPopulationMeasureReports(result, reportConfigurations) - _measureReportPopulationResultList.addAll( - measureReportPopulationResultList, - ) - } - .onSuccess { - measureReportPopulationResults.value = _measureReportPopulationResultList - Timber.w("measureReportPopulationResults${measureReportPopulationResults.value}") - toggleProgressIndicatorVisibility(false) - // Show results of measure report for individual/population - navController.navigate(MeasureReportNavigationScreen.MeasureReportResult.route) { - launchSingleTop = true + // if report is of current month or does not exist generate a new one and replace + // existing + if ( + endDateFormatted + .parseDate(SDF_YYYY_MM_DD)!! + .formatDate(SDF_YYYY_MMM) + .contentEquals(Date().formatDate(SDF_YYYY_MMM)) || + existingValidReports.isEmpty() || + existingValidReports.size != subjects.size + ) { + fhirEngine.loadCqlLibraryBundle(fhirOperator, config.url) + + measureReportRepository.evaluatePopulationMeasure( + measureUrl = config.url, + startDateFormatted = startDateFormatted, + endDateFormatted = endDateFormatted, + subjects = subjects, + existing = existingValidReports, + practitionerId = practitionerId, + ) + } else { + existingValidReports } } - .onFailure { - Timber.w(it) - toggleProgressIndicatorVisibility(false) - } + + val measureReportPopulationResultList = + formatPopulationMeasureReports(result, reportConfigurations) + _measureReportPopulationResultList.addAll( + measureReportPopulationResultList, + ) + + // On success + measureReportPopulationResults.value = _measureReportPopulationResultList + // Timber.w("measureReportPopulationResults${measureReportPopulationResults.value}") + + // Show results of measure report for individual/population + navController.navigate(MeasureReportNavigationScreen.MeasureReportResult.route) { + launchSingleTop = true + } + } catch (e: Exception) { + Timber.e(e) + } finally { + toggleProgressIndicatorVisibility(false) } } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt index 37e07977c8..f6b790b2ca 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt @@ -92,7 +92,6 @@ import org.smartregister.fhircore.quest.data.report.measure.MeasureReportPagingS import org.smartregister.fhircore.quest.data.report.measure.MeasureReportRepository import org.smartregister.fhircore.quest.navigation.MeasureReportNavigationScreen import org.smartregister.fhircore.quest.robolectric.RobolectricTest -import org.smartregister.fhircore.quest.ui.report.measure.models.MeasureReportPopulationResult import org.smartregister.fhircore.quest.ui.shared.models.MeasureReportSubjectViewData import org.smartregister.fhircore.quest.util.mappers.MeasureReportSubjectViewDataMapper @@ -188,7 +187,7 @@ class MeasureReportViewModelTest : RobolectricTest() { url = "http://nourl.com", module = "Module1", ) - every { measureReportViewModel.evaluateMeasure(any(), any()) } just runs + coEvery { measureReportViewModel.evaluateMeasure(any(), any()) } just runs measureReportViewModel.reportTypeSelectorUiState.value = ReportTypeSelectorUiState(startDate = "21 Jan, 2022", endDate = "27 Jan, 2022") measureReportViewModel.onEvent( @@ -204,7 +203,7 @@ class MeasureReportViewModelTest : RobolectricTest() { Assert.assertEquals(viewModelConfig.first().id, reportConfiguration.id) Assert.assertEquals(viewModelConfig.first().module, reportConfiguration.module) - verify { + coVerify { measureReportViewModel.evaluateMeasure( navController = navController, practitionerId = "practitioner-id", @@ -309,7 +308,11 @@ class MeasureReportViewModelTest : RobolectricTest() { @Test fun testEvaluateMeasureUtilizesPreviouslyGeneratedMeasureReportIfAvailable() = runTest(timeout = 90.seconds, context = unconfinedTestDispatcher) { - val subject = Group().apply { id = "groupId" } + val subject = + Group().apply { + id = "groupId" + name = "Test Group" + } val testMeasureReport = MeasureReport().apply { id = "measureId" @@ -333,9 +336,6 @@ class MeasureReportViewModelTest : RobolectricTest() { module = "Module1", ) - coEvery { measureReportViewModel.formatPopulationMeasureReports(any(), any()) } returns - listOf(MeasureReportPopulationResult()) - coEvery { fhirEngine.retrievePreviouslyGeneratedMeasureReports( startDateFormatted = "2022-01-21", @@ -369,7 +369,8 @@ class MeasureReportViewModelTest : RobolectricTest() { measureReportViewModel.formatPopulationMeasureReports(listOf(testMeasureReport), any()) } - coVerify(exactly = 0) { + // TODO Confirm verification as per implementation + coVerify { measureReportRepository.evaluatePopulationMeasure(any(), any(), any(), any(), any(), any()) } } From ff1e3c65da8d4f8eff49fcda9a61978e5aefd290 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 2 Sep 2024 14:39:59 +0300 Subject: [PATCH 084/110] Clean up gradle dependencies configuration --- android/engine/build.gradle.kts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index f70ab00aa9..21dac68879 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -184,8 +184,8 @@ dependencies { api(libs.data.capture) { isTransitive = true exclude(group = "ca.uhn.hapi.fhir") - exclude(group = "com.google.android.fhir", module = "engine") exclude(group = "com.google.android.fhir", module = "common") + exclude(group = "org.smartregister", module = "common") exclude(group = "org.slf4j", module = "jcl-over-slf4j") } api(libs.cqf.fhir.cr) { @@ -198,23 +198,19 @@ dependencies { exclude(group = "xerces") exclude(group = "com.github.java-json-tools") exclude(group = "org.codehaus.woodstox") - exclude(group = "com.google.android.fhir", module = "common") exclude(group = "com.google.android.fhir", module = "engine") + exclude(group = "org.smartregister", module = "engine") exclude(group = "com.github.ben-manes.caffeine") } api(libs.contrib.barcode) { isTransitive = true exclude(group = "org.smartregister", module = "data-capture") exclude(group = "ca.uhn.hapi.fhir") - exclude(group = "com.google.android.fhir", module = "common") - exclude(group = "com.google.android.fhir", module = "engine") } api(libs.contrib.locationwidget) { isTransitive = true exclude(group = "org.smartregister", module = "data-capture") exclude(group = "ca.uhn.hapi.fhir") - exclude(group = "com.google.android.fhir", module = "common") - exclude(group = "com.google.android.fhir", module = "engine") } api(libs.fhir.engine) { isTransitive = true From 9a96a82593f152db00f8dc367f93071a94feb270 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 2 Sep 2024 14:40:49 +0300 Subject: [PATCH 085/110] Move measure reporting evaluation to BG thread --- .../report/measure/MeasureReportRepository.kt | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index 110da18a5b..cbbe7b8527 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -25,6 +25,7 @@ import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject +import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.Measure @@ -35,6 +36,7 @@ import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.configuration.report.measure.ReportConfiguration import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor +import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -54,6 +56,7 @@ constructor( override val fhirPathDataExtractor: FhirPathDataExtractor, override val parser: IParser, @ApplicationContext override val context: Context, + val dispatcherProvider: DispatcherProvider, ) : DefaultRepository( fhirEngine = fhirEngine, @@ -146,17 +149,19 @@ constructor( subject: String?, practitionerId: String?, ): MeasureReport { - return fhirOperator.evaluateMeasure( - measure = - knowledgeManager - .loadResources(ResourceType.Measure.name, measureUrl, null, null, null) - .firstOrNull() as Measure, - start = startDateFormatted, - end = endDateFormatted, - reportType = reportType, - subjectId = subject, - practitioner = practitionerId.takeIf { it?.isNotBlank() == true }, - ) + return withContext(dispatcherProvider.io()) { + fhirOperator.evaluateMeasure( + measure = + knowledgeManager + .loadResources(ResourceType.Measure.name, measureUrl, null, null, null) + .firstOrNull() as Measure, + start = startDateFormatted, + end = endDateFormatted, + reportType = reportType, + subjectId = subject, + practitioner = practitionerId.takeIf { it?.isNotBlank() == true }, + ) + } } /** From 8ff7951110380fd22cfe81733341f1af25e05558 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 2 Sep 2024 15:05:06 +0300 Subject: [PATCH 086/110] Fix MeasureReportRepositoryTest --- .../quest/data/report/measure/MeasureReportRepositoryTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt index b0c43d33f4..8d0bc2e1b0 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt @@ -135,6 +135,7 @@ class MeasureReportRepositoryTest : RobolectricTest() { fhirPathDataExtractor = mockk(), parser = parser, context = ApplicationProvider.getApplicationContext(), + dispatcherProvider = dispatcherProvider ) } From 6c564197707efcd8fc25080d7b713ff6d6e01a87 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 2 Sep 2024 15:45:50 +0300 Subject: [PATCH 087/110] Fix MeasureReportRepositoryTest --- .../quest/data/report/measure/MeasureReportRepositoryTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt index 8d0bc2e1b0..eced4b1e16 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepositoryTest.kt @@ -135,7 +135,7 @@ class MeasureReportRepositoryTest : RobolectricTest() { fhirPathDataExtractor = mockk(), parser = parser, context = ApplicationProvider.getApplicationContext(), - dispatcherProvider = dispatcherProvider + dispatcherProvider = dispatcherProvider, ) } From 4057e11ff6c8b4e1bde56d9dfeda3fd2fd3b93e8 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 3 Sep 2024 17:59:43 +0300 Subject: [PATCH 088/110] =?UTF-8?q?Fix=20MeasureReportViewModel=20unit=20t?= =?UTF-8?q?ests=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Roy Munge --- .../report/measure/MeasureReportViewModel.kt | 3 +- .../measure/MeasureReportViewModelTest.kt | 43 +++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt index fe1769d270..302bfcd1a3 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModel.kt @@ -289,7 +289,8 @@ constructor( val subjects = mutableListOf() subjects.addAll(measureReportRepository.fetchSubjects(config)) - // If a practitioner Id is available, add it to the list of subjects + // If a practitioner Id is available and if the subjects list is empty, add it to the + // list of subjects if (practitionerId?.isNotBlank() == true && subjects.isEmpty()) { subjects.add("${Practitioner().resourceType.name}/$practitionerId") } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt index f6b790b2ca..5949ebf6e7 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/report/measure/MeasureReportViewModelTest.kt @@ -315,7 +315,7 @@ class MeasureReportViewModelTest : RobolectricTest() { } val testMeasureReport = MeasureReport().apply { - id = "measureId" + id = "MeasureReport/measureId" measure = "http://nourl.com" type = MeasureReportType.INDIVIDUAL this.subject = subject.asReference() @@ -329,13 +329,16 @@ class MeasureReportViewModelTest : RobolectricTest() { val reportConfiguration = ReportConfiguration( - id = "measureId", + id = "ReportConfiguration/measureId", title = "Measure 1", description = "Measure report for testing", url = "http://nourl.com", module = "Module1", ) + coEvery { measureReportViewModel.formatPopulationMeasureReports(any(), any()) } returns + emptyList() + coEvery { fhirEngine.retrievePreviouslyGeneratedMeasureReports( startDateFormatted = "2022-01-21", @@ -345,19 +348,8 @@ class MeasureReportViewModelTest : RobolectricTest() { ) } returns listOf(testMeasureReport) - coEvery { - measureReportRepository.evaluatePopulationMeasure( - startDateFormatted = any(), - endDateFormatted = any(), - measureUrl = any(), - subjects = any(), - existing = any(), - practitionerId = any(), - ) - } returns listOf(testMeasureReport) - coEvery { measureReportRepository.fetchSubjects(any(ReportConfiguration::class)) } returns - listOf() + listOf(subject.asReference().toString()) measureReportViewModel.reportTypeSelectorUiState.value = ReportTypeSelectorUiState(startDate = "21 Jan, 2022", endDate = "27 Jan, 2022") @@ -365,12 +357,29 @@ class MeasureReportViewModelTest : RobolectricTest() { measureReportViewModel.evaluateMeasure(navController, null) + val measureReportListSlot = slot>() + coVerify { - measureReportViewModel.formatPopulationMeasureReports(listOf(testMeasureReport), any()) + measureReportViewModel.formatPopulationMeasureReports(capture(measureReportListSlot), any()) } - // TODO Confirm verification as per implementation - coVerify { + assertEquals(testMeasureReport.id, measureReportListSlot.captured.first().id) + assertEquals(testMeasureReport.measure, measureReportListSlot.captured.first().measure) + assertEquals(testMeasureReport.type, measureReportListSlot.captured.first().type) + assertEquals( + testMeasureReport.subject.reference, + measureReportListSlot.captured.first().subject.reference, + ) + assertEquals( + testMeasureReport.period.start.toString(), + measureReportListSlot.captured.first().period.start.toString(), + ) + assertEquals( + testMeasureReport.period.end.toString(), + measureReportListSlot.captured.first().period.end.toString(), + ) + + coVerify(exactly = 0) { measureReportRepository.evaluatePopulationMeasure(any(), any(), any(), any(), any(), any()) } } From 74f754d78a9a2cfa1f8b432f4ae25c5ffcf2ce77 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 9 Sep 2024 09:53:05 +0300 Subject: [PATCH 089/110] Fix Measure Reporting --- .../engine/configuration/ConfigurationRegistry.kt | 2 +- .../engine/task/FhirCarePlanGeneratorTest.kt | 2 +- android/gradle/libs.versions.toml | 12 ++++++------ .../data/report/measure/MeasureReportRepository.kt | 7 +------ .../smartregister/fhircore/quest/CqlContentTest.kt | 2 +- .../ui/questionnaire/QuestionnaireViewModelTest.kt | 2 +- 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index 5aebbe77ca..bd9c71ee98 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -602,7 +602,7 @@ constructor( */ try { if (resource is MetadataResource && resource.name != null) { - knowledgeManager.install( + knowledgeManager.index( writeToFile(resource.overwriteCanonicalURL()), ) } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt index fec08a518b..908a5aa07c 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt @@ -2415,7 +2415,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { } private suspend fun installToIgManager(resource: Resource) { - knowledgeManager.install(writeToFile(resource)) + knowledgeManager.index(writeToFile(resource)) } private suspend fun loadResource( diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index f79ae0326a..0747a2dd0e 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -6,7 +6,7 @@ androidx-camera = "1.4.0-rc01" androidx-paging = "3.3.2" androidx-test= "1.6.1" appcompat = "1.7.0" -benchmark-junit = "1.2.4" +benchmark-junit = "1.3.0" cardview = "1.0.0" commonsJexl3 = "3.2.1" compose-material-icons = "1.6.8" @@ -20,17 +20,17 @@ coverallsGradlePlugin = "2.12.2" cqfFhirCr = "3.8.0" dagger-hilt = "2.51" datastore = "1.1.1" -desugar-jdk-libs = "2.0.4" +desugar-jdk-libs = "2.1.2" dokkaBase = "1.9.20" easy-rules-jexl = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview13.1-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview12-SNAPSHOT" -fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview11-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview14-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview13-SNAPSHOT" +fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview12-SNAPSHOT" foundation = "1.6.8" fragment-ktx = "1.8.2" glide = "4.16.0" diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index cbbe7b8527..c072361ff4 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -28,9 +28,7 @@ import javax.inject.Inject import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.model.Group -import org.hl7.fhir.r4.model.Measure import org.hl7.fhir.r4.model.MeasureReport -import org.hl7.fhir.r4.model.ResourceType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.configuration.report.measure.ReportConfiguration @@ -151,10 +149,7 @@ constructor( ): MeasureReport { return withContext(dispatcherProvider.io()) { fhirOperator.evaluateMeasure( - measure = - knowledgeManager - .loadResources(ResourceType.Measure.name, measureUrl, null, null, null) - .firstOrNull() as Measure, + measureUrl = measureUrl, start = startDateFormatted, end = endDateFormatted, reportType = reportType, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index 012d5faccb..b610c16754 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -197,7 +197,7 @@ class CqlContentTest : RobolectricTest() { private suspend fun createTestData(dataBundle: Bundle, cqlLibrary: Library) { dataBundle.entry.forEach { fhirEngine.create(it.resource) } - knowledgeManager.install( + knowledgeManager.index( File.createTempFile(cqlLibrary.name, ".json").apply { this.writeText(cqlLibrary.encodeResourceToString()) }, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 6b02918404..06e7ceb597 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -1081,7 +1081,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { ) } - knowledgeManager.install( + knowledgeManager.index( File.createTempFile(cqlLibrary.name, ".json").apply { this.writeText(cqlLibrary.encodeResourceToString()) }, From 5e6b2030ab9fc429d61530d8103698b673d408e7 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 10 Sep 2024 15:34:55 +0300 Subject: [PATCH 090/110] Migrate Engine and Workflow libraries - Fix Measure reporting - Optimization PRs added --- android/gradle/libs.versions.toml | 4 ++-- .../quest/data/report/measure/MeasureReportRepository.kt | 6 +++++- .../quest/ui/report/measure/worker/MeasureReportWorker.kt | 8 +++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 0747a2dd0e..bacb136e52 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -28,9 +28,9 @@ fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview14-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview13-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview14-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview12-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview13-SNAPSHOT" foundation = "1.6.8" fragment-ktx = "1.8.2" glide = "4.16.0" diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index c072361ff4..b6731f693e 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -27,7 +27,9 @@ import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException +import org.hl7.fhir.instance.model.api.IBaseResource import org.hl7.fhir.r4.model.Group +import org.hl7.fhir.r4.model.Measure import org.hl7.fhir.r4.model.MeasureReport import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.app.ConfigService @@ -148,8 +150,10 @@ constructor( practitionerId: String?, ): MeasureReport { return withContext(dispatcherProvider.io()) { + val measureUrlResources: Iterable = knowledgeManager.loadResources(measureUrl) + fhirOperator.evaluateMeasure( - measureUrl = measureUrl, + measure = measureUrlResources.first() as Measure, start = startDateFormatted, end = endDateFormatted, reportType = reportType, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt index c6f272384c..b48ee9646b 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt @@ -25,6 +25,7 @@ import androidx.work.WorkManager import androidx.work.WorkerParameters import androidx.work.workDataOf import com.google.android.fhir.FhirEngine +import com.google.android.fhir.knowledge.KnowledgeManager import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.assisted.Assisted @@ -38,6 +39,7 @@ import java.util.Calendar import java.util.Date import java.util.concurrent.TimeUnit import kotlinx.coroutines.withContext +import org.hl7.fhir.instance.model.api.IBaseResource import org.hl7.fhir.r4.model.Measure import org.hl7.fhir.r4.model.MeasureReport import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry @@ -67,6 +69,7 @@ constructor( val dispatcherProvider: DefaultDispatcherProvider, val fhirOperator: FhirOperator, val fhirEngine: FhirEngine, + private val knowledgeManager: KnowledgeManager, val workManager: WorkManager, ) : CoroutineWorker(appContext, workerParams) { @@ -127,8 +130,11 @@ constructor( val measureReport: MeasureReport? = withContext(dispatcherProvider.io()) { try { + val measureUrlResources: Iterable = + knowledgeManager.loadResources(measureUrl) + fhirOperator.evaluateMeasure( - measureUrl = measureUrl, + measure = measureUrlResources.first() as Measure, start = startDateFormatted, end = endDateFormatted, reportType = MeasureReportViewModel.POPULATION, From a1ef8cda3740d1b05bb80523908050af55b65ed4 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 11 Sep 2024 09:27:59 +0300 Subject: [PATCH 091/110] Update Workflow library - Fix build - Fix reporting NPE --- android/gradle/libs.versions.toml | 2 +- .../org/smartregister/fhircore/quest/integration/Faker.kt | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index bacb136e52..06768acd95 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -30,7 +30,7 @@ fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview14-SNAPSHOT" fhir-sdk-engine = "1.0.0-preview14-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview13-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview13.1-SNAPSHOT" foundation = "1.6.8" fragment-ktx = "1.8.2" glide = "4.16.0" diff --git a/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt b/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt index 1350636997..01081c3ca8 100644 --- a/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt +++ b/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt @@ -18,6 +18,7 @@ package org.smartregister.fhircore.quest.integration import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry +import com.google.android.fhir.CrudFhirEngine import com.google.android.fhir.FhirEngine import com.google.android.fhir.LocalChange import com.google.android.fhir.SearchResult @@ -109,6 +110,10 @@ object Faker { } override suspend fun update(vararg resource: Resource) {} + + override suspend fun withTransaction(block: suspend CrudFhirEngine.() -> Unit) { + TODO("Not yet implemented") + } } val fhirResourceService = From ae6708d2e1615b2eef5c0df0c3c817aee1486dd1 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 11 Sep 2024 18:58:45 +0300 Subject: [PATCH 092/110] Fix evaluate Population Measure --- .../report/measure/MeasureReportRepository.kt | 32 +++++++++++++------ .../measure/worker/MeasureReportWorker.kt | 4 +++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index b6731f693e..0b4dede76c 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -24,6 +24,7 @@ import com.google.android.fhir.knowledge.KnowledgeManager import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.qualifiers.ApplicationContext +import java.util.NoSuchElementException import javax.inject.Inject import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException @@ -127,6 +128,8 @@ constructor( } } catch (exception: NullPointerException) { Timber.e(exception, "Exception thrown with measureUrl: $measureUrl.") + } catch (exception: IllegalStateException) { + Timber.e(exception, "Exception thrown with measureUrl: $measureUrl.") } return measureReport } @@ -150,16 +153,27 @@ constructor( practitionerId: String?, ): MeasureReport { return withContext(dispatcherProvider.io()) { - val measureUrlResources: Iterable = knowledgeManager.loadResources(measureUrl) + try { + val measureUrlResources: Iterable = + knowledgeManager.loadResources(measureUrl) - fhirOperator.evaluateMeasure( - measure = measureUrlResources.first() as Measure, - start = startDateFormatted, - end = endDateFormatted, - reportType = reportType, - subjectId = subject, - practitioner = practitionerId.takeIf { it?.isNotBlank() == true }, - ) + fhirOperator.evaluateMeasure( + measure = measureUrlResources.first() as Measure, + start = startDateFormatted, + end = endDateFormatted, + reportType = reportType, + subjectId = subject, + practitioner = practitionerId.takeIf { it?.isNotBlank() == true }, + ) + } catch (exception: IllegalArgumentException) { + Timber.e(exception) + throw IllegalArgumentException() + } catch (exception: NoSuchElementException) { + Timber.e(exception) + throw IllegalStateException( + "No Measure library found in Knowledge Manager with URL $measureUrl", + ) + } } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt index b48ee9646b..196d042907 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt @@ -37,6 +37,7 @@ import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit import java.util.Calendar import java.util.Date +import java.util.NoSuchElementException import java.util.concurrent.TimeUnit import kotlinx.coroutines.withContext import org.hl7.fhir.instance.model.api.IBaseResource @@ -146,6 +147,9 @@ constructor( } catch (exception: IllegalArgumentException) { Timber.e(exception) null + } catch (exception: NoSuchElementException) { + Timber.e(exception) + null } } if (measureReport != null) { From afb4ade5c151b4130b66a7e33807aab792e789c2 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 13 Sep 2024 13:51:23 +0300 Subject: [PATCH 093/110] Update SDC snapshot to 14.1 --- android/gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 06768acd95..f55c14434c 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -27,7 +27,7 @@ espresso-core = "3.6.1" fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview14-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview14.1-SNAPSHOT" fhir-sdk-engine = "1.0.0-preview14-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" fhir-sdk-workflow = "0.1.0-alpha04-preview13.1-SNAPSHOT" From 3d7b2e29cfe04de5b02bd0c3578c552bdf76ff36 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 13 Sep 2024 13:55:13 +0300 Subject: [PATCH 094/110] Remove unrecommended forced portrait format --- android/quest/src/main/AndroidManifest.xml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/android/quest/src/main/AndroidManifest.xml b/android/quest/src/main/AndroidManifest.xml index 2c2d7676f4..4c4c626207 100644 --- a/android/quest/src/main/AndroidManifest.xml +++ b/android/quest/src/main/AndroidManifest.xml @@ -9,18 +9,17 @@ + android:exported="true"> @@ -47,31 +45,26 @@ + android:launchMode="singleTop" /> + android:launchMode="singleTop" /> + android:launchMode="singleTop" /> From affa880493b1ceff3b1a26daec57e34a9aa927de Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 13 Sep 2024 14:02:47 +0300 Subject: [PATCH 095/110] Refactor from using deprecated KnowledgeManager methods loadResources --- .../quest/data/report/measure/MeasureReportRepository.kt | 2 +- .../fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt | 3 +-- .../quest/ui/report/measure/worker/MeasureReportWorker.kt | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index 0b4dede76c..d026a61299 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -155,7 +155,7 @@ constructor( return withContext(dispatcherProvider.io()) { try { val measureUrlResources: Iterable = - knowledgeManager.loadResources(measureUrl) + knowledgeManager.loadResources(url = measureUrl) fhirOperator.evaluateMeasure( measure = measureUrlResources.first() as Measure, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index ae70ed686b..96ed4c9654 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -785,8 +785,7 @@ constructor( val librariesList = withContext(dispatcherProvider.io()) { knowledgeManager.loadResources( - ResourceType.Library.name, - libraryUrl, + url = libraryUrl, ) } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt index 196d042907..4cc59a62bd 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt @@ -132,7 +132,7 @@ constructor( withContext(dispatcherProvider.io()) { try { val measureUrlResources: Iterable = - knowledgeManager.loadResources(measureUrl) + knowledgeManager.loadResources(url = measureUrl) fhirOperator.evaluateMeasure( measure = measureUrlResources.first() as Measure, From 37a462bb7bea2059740b2fcf97ac2091e26b20be Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Mon, 16 Sep 2024 18:42:06 +0300 Subject: [PATCH 096/110] Clean up TOML catalog file --- android/engine/build.gradle.kts | 2 +- android/gradle/libs.versions.toml | 37 +++++++++---------- android/quest/build.gradle.kts | 2 +- .../report/measure/MeasureReportRepository.kt | 2 +- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index 21dac68879..df9f61947b 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -125,7 +125,7 @@ configurations { dependencies { implementation(libs.play.services.tasks) - implementation(libs.gms.play.services.location) + implementation(libs.play.services.location) coreLibraryDesugaring(libs.core.desugar) // Library dependencies diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index f55c14434c..45a2f6a1d6 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -4,12 +4,12 @@ activity-compose = "1.8.2" androidJunit5 = "1.8.2.1" androidx-camera = "1.4.0-rc01" androidx-paging = "3.3.2" -androidx-test= "1.6.1" +androidx-test= "1.6.2" appcompat = "1.7.0" benchmark-junit = "1.3.0" cardview = "1.0.0" commonsJexl3 = "3.2.1" -compose-material-icons = "1.6.8" +compose-ui = "1.6.3" compressor = "3.0.1" constraintlayout = "2.1.4" constraintlayout-compose = "1.0.1" @@ -28,10 +28,9 @@ fhir-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview14.1-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview14-SNAPSHOT" -fhir-sdk-knowledge = "0.1.0-beta01-preview-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview13.1-SNAPSHOT" -foundation = "1.6.8" +fhir-sdk-engine = "1.0.0-preview14.2-SNAPSHOT" +fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview10.2DEV-SNAPSHOT" fragment-ktx = "1.8.2" glide = "4.16.0" gradle = "8.3.2" @@ -52,10 +51,10 @@ kotlinx-serialization-json = "1.6.0" kt3k-coveralls-ver="2.12.0" ktlint = "0.50.0" kujaku-library = "0.10.5-SNAPSHOT" +kujaku-mapbox-sdk-turf = "4.8.0" leakcanary-android = "2.10" -lifecycle= "2.8.4" +lifecycle= "2.8.5" logback-android = "3.0.0" -mapbox-sdk-turf = "4.8.0" material = "1.12.0" mlkit-barcode-scanning = "17.3.0" mockk = "1.13.8" @@ -81,7 +80,6 @@ slf4j-nop = "2.0.7" spotlessPluginGradle = "6.25.0" stax-api = "1.0-2" timber = "5.0.1" -ui = "1.6.3" uiautomator = "2.3.0" work = "2.9.1" xercesImpl = "2.12.2" @@ -100,8 +98,8 @@ appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "a benchmark-junit = { group = "androidx.benchmark", name = "benchmark-junit4", version.ref = "benchmark-junit" } cardview = { group = "androidx.cardview", name = "cardview", version.ref = "cardview" } commons-jexl3 = { module = "org.apache.commons:commons-jexl3", version.ref = "commonsJexl3" } -compose-material-icons-core = { group = "androidx.compose.material", name = "material-icons-core", version.ref = "compose-material-icons" } -compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "compose-material-icons" } +compose-material-icons-core = { group = "androidx.compose.material", name = "material-icons-core", version.ref = "compose-ui" } +compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "compose-ui" } compressor = { group = "id.zelory", name = "compressor", version.ref = "compressor" } constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } constraintlayout-compose = { group = "androidx.constraintlayout", name = "constraintlayout-compose", version.ref = "constraintlayout-compose" } @@ -125,11 +123,10 @@ easy-rules-jexl = { group = "org.smartregister", name = "easy-rules-jexl", versi espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } fhir-common-utils = { group = "org.smartregister", name = "fhir-common-utils", version.ref = "fhir-common-utils" } fhir-engine = { group = "org.smartregister", name = "engine", version.ref = "fhir-sdk-engine" } -foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" } +foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "compose-ui" } fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment-ktx" } fragment-testing = { group = "androidx.fragment", name = "fragment-testing", version.ref = "fragment-ktx" } glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" } -gms-play-services-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "playServicesLocation" } gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hilt" } @@ -160,7 +157,7 @@ lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-lived lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" } logback-android = { module = "com.github.tony19:logback-android", version.ref = "logback-android" } -mapbox-sdk-turf = { group = "com.mapbox.mapboxsdk", name = "mapbox-sdk-turf", version.ref = "mapbox-sdk-turf" } +mapbox-sdk-turf = { group = "com.mapbox.mapboxsdk", name = "mapbox-sdk-turf", version.ref = "kujaku-mapbox-sdk-turf" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } mlkit-barcode-scanning = { group = "com.google.mlkit", name = "barcode-scanning", version.ref = "mlkit-barcode-scanning"} mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } @@ -186,16 +183,16 @@ retrofit2-kotlinx-serialization-converter = { group = "com.jakewharton.retrofit" robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } rules = { group = "androidx.test", name = "rules", version.ref = "rules" } runner = { module = "androidx.test:runner", version.ref = "androidx-test" } -runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "ui" } +runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "compose-ui" } security-crypto = { group = "androidx.security", name = "security-crypto", version.ref = "security-crypto" } slf4j-nop = { group = "org.slf4j", name = "slf4j-nop", version.ref = "slf4j-nop" } stax-api = { group = "javax.xml.stream", name = "stax-api", version.ref = "stax-api" } timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } -ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" } -ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "ui" } -ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "ui" } -ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "ui" } -ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "ui" } +ui = { group = "androidx.compose.ui", name = "ui", version.ref = "compose-ui" } +ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "compose-ui" } +ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "compose-ui" } +ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose-ui" } +ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "compose-ui" } uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" } work-testing = { group = "androidx.work", name = "work-testing", version.ref = "work" } diff --git a/android/quest/build.gradle.kts b/android/quest/build.gradle.kts index e4dd623b4c..460a8b93cd 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -435,7 +435,7 @@ dependencies { implementation(libs.material) implementation(libs.dagger.hilt.android) implementation(libs.hilt.work) - implementation(libs.gms.play.services.location) + implementation(libs.play.services.location) implementation(libs.mlkit.barcode.scanning) implementation(libs.bundles.cameraX) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index d026a61299..27178ac638 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -171,7 +171,7 @@ constructor( } catch (exception: NoSuchElementException) { Timber.e(exception) throw IllegalStateException( - "No Measure library found in Knowledge Manager with URL $measureUrl", + "No FHIR resource found in Knowledge Manager with URL $measureUrl", ) } } From d3250b244e25c3333c087f0cde9f4f3ac96bb106 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 17 Sep 2024 08:55:00 +0300 Subject: [PATCH 097/110] Revert Knowledge Manager and Workflow Library Upgrades --- .../engine/configuration/ConfigurationRegistry.kt | 2 +- .../fhircore/engine/task/WorkflowCarePlanGenerator.kt | 7 ++++--- .../fhircore/engine/task/FhirCarePlanGeneratorTest.kt | 2 +- android/gradle/libs.versions.toml | 8 ++++---- .../data/report/measure/MeasureReportRepository.kt | 10 +++++----- .../quest/ui/questionnaire/QuestionnaireViewModel.kt | 1 + .../ui/report/measure/worker/MeasureReportWorker.kt | 6 +++++- .../org/smartregister/fhircore/quest/CqlContentTest.kt | 2 +- .../ui/questionnaire/QuestionnaireViewModelTest.kt | 2 +- 9 files changed, 23 insertions(+), 17 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index bd9c71ee98..5aebbe77ca 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -602,7 +602,7 @@ constructor( */ try { if (resource is MetadataResource && resource.name != null) { - knowledgeManager.index( + knowledgeManager.install( writeToFile(resource.overwriteCanonicalURL()), ) } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt index 641edb2f42..c40b1a092a 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/task/WorkflowCarePlanGenerator.kt @@ -41,8 +41,7 @@ import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.Task import org.hl7.fhir.r4.utils.FHIRPathEngine import org.opencds.cqf.fhir.cql.LibraryEngine -import org.opencds.cqf.fhir.cr.plandefinition.PlanDefinitionProcessor -import org.opencds.cqf.fhir.utility.monad.Eithers +import org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor import org.opencds.cqf.fhir.utility.r4.Parameters.part import org.smartregister.fhircore.engine.data.local.DefaultRepository import timber.log.Timber @@ -129,7 +128,9 @@ constructor( params.addParameter(part("%subject", subject)) return planDefProcessor.apply( - Eithers.forRight3(planDefinition), + null, + null, + planDefinition, "Patient/${subject.id}", null, null, diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt index 908a5aa07c..fec08a518b 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt @@ -2415,7 +2415,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { } private suspend fun installToIgManager(resource: Resource) { - knowledgeManager.index(writeToFile(resource)) + knowledgeManager.install(writeToFile(resource)) } private suspend fun loadResource( diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 45a2f6a1d6..9d10da97b3 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -17,20 +17,20 @@ converter-gson = "2.9.0" core-ktx = "1.13.1" core-testing = "2.2.0" coverallsGradlePlugin = "2.12.2" -cqfFhirCr = "3.8.0" +cqfFhirCr = "3.0.0-PRE9" dagger-hilt = "2.51" datastore = "1.1.1" desugar-jdk-libs = "2.1.2" dokkaBase = "1.9.20" easy-rules-jexl = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" -fhir-common-utils = "1.0.0-SNAPSHOT" +fhir-sdk-common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview14.1-SNAPSHOT" fhir-sdk-engine = "1.0.0-preview14.2-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview10.2DEV-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview10-SNAPSHOT" fragment-ktx = "1.8.2" glide = "4.16.0" gradle = "8.3.2" @@ -121,7 +121,7 @@ datastore-preferences = { group = "androidx.datastore", name = "datastore-prefer dokka-base = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokkaBase" } easy-rules-jexl = { group = "org.smartregister", name = "easy-rules-jexl", version.ref = "easy-rules-jexl" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } -fhir-common-utils = { group = "org.smartregister", name = "fhir-common-utils", version.ref = "fhir-common-utils" } +fhir-common-utils = { group = "org.smartregister", name = "fhir-common-utils", version.ref = "fhir-sdk-common-utils" } fhir-engine = { group = "org.smartregister", name = "engine", version.ref = "fhir-sdk-engine" } foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "compose-ui" } fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment-ktx" } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index 27178ac638..0c2cdeabf5 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -28,10 +28,10 @@ import java.util.NoSuchElementException import javax.inject.Inject import kotlinx.coroutines.withContext import org.hl7.fhir.exceptions.FHIRException -import org.hl7.fhir.instance.model.api.IBaseResource import org.hl7.fhir.r4.model.Group import org.hl7.fhir.r4.model.Measure import org.hl7.fhir.r4.model.MeasureReport +import org.hl7.fhir.r4.model.ResourceType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.configuration.report.measure.ReportConfiguration @@ -154,11 +154,11 @@ constructor( ): MeasureReport { return withContext(dispatcherProvider.io()) { try { - val measureUrlResources: Iterable = - knowledgeManager.loadResources(url = measureUrl) - fhirOperator.evaluateMeasure( - measure = measureUrlResources.first() as Measure, + measure = + knowledgeManager + .loadResources(ResourceType.Measure.name, measureUrl, null, null, null) + .firstOrNull() as Measure, start = startDateFormatted, end = endDateFormatted, reportType = reportType, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 96ed4c9654..91e1a40f90 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -785,6 +785,7 @@ constructor( val librariesList = withContext(dispatcherProvider.io()) { knowledgeManager.loadResources( + resourceType = ResourceType.Library.name, url = libraryUrl, ) } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt index 4cc59a62bd..5a795b568f 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/report/measure/worker/MeasureReportWorker.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.withContext import org.hl7.fhir.instance.model.api.IBaseResource import org.hl7.fhir.r4.model.Measure import org.hl7.fhir.r4.model.MeasureReport +import org.hl7.fhir.r4.model.ResourceType import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.data.local.DefaultRepository import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider @@ -132,7 +133,10 @@ constructor( withContext(dispatcherProvider.io()) { try { val measureUrlResources: Iterable = - knowledgeManager.loadResources(url = measureUrl) + knowledgeManager.loadResources( + resourceType = ResourceType.Measure.name, + url = measureUrl, + ) fhirOperator.evaluateMeasure( measure = measureUrlResources.first() as Measure, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index b610c16754..012d5faccb 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -197,7 +197,7 @@ class CqlContentTest : RobolectricTest() { private suspend fun createTestData(dataBundle: Bundle, cqlLibrary: Library) { dataBundle.entry.forEach { fhirEngine.create(it.resource) } - knowledgeManager.index( + knowledgeManager.install( File.createTempFile(cqlLibrary.name, ".json").apply { this.writeText(cqlLibrary.encodeResourceToString()) }, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 06e7ceb597..6b02918404 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -1081,7 +1081,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { ) } - knowledgeManager.index( + knowledgeManager.install( File.createTempFile(cqlLibrary.name, ".json").apply { this.writeText(cqlLibrary.encodeResourceToString()) }, From 9774be91c574b2c71907461dd44a2a05d58f4d4f Mon Sep 17 00:00:00 2001 From: fikrimilano Date: Wed, 18 Sep 2024 15:25:54 +0700 Subject: [PATCH 098/110] Display symbol instead of unicode --- android/quest/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/quest/src/main/res/values/strings.xml b/android/quest/src/main/res/values/strings.xml index 9a21e34727..adb04213d9 100644 --- a/android/quest/src/main/res/values/strings.xml +++ b/android/quest/src/main/res/values/strings.xml @@ -132,5 +132,5 @@ Scan QR Code Place your camera over the entire QR Code to start scanning Failed to get GPS location - \\u0020\\u002a + \u0020\u002a From c9d2bde29d5d7e65d7015dd430aeeefd3585c347 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 18 Sep 2024 16:09:57 +0300 Subject: [PATCH 099/110] Refactor usage of FHIR JSONParser to support concurrency --- .../fhircore/engine/configuration/ConfigurationRegistry.kt | 3 +-- .../fhircore/engine/p2p/dao/BaseP2PTransferDao.kt | 4 ---- .../fhircore/engine/p2p/dao/P2PReceiverTransferDao.kt | 5 ++++- .../fhircore/engine/p2p/dao/P2PSenderTransferDao.kt | 3 ++- .../fhircore/engine/rulesengine/RulesFactory.kt | 6 +++--- .../quest/ui/questionnaire/QuestionnaireViewModel.kt | 5 +++-- .../java/org/smartregister/fhircore/quest/sdk/CqlBuilder.kt | 2 -- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt index 5aebbe77ca..a7a4032bf7 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt @@ -107,7 +107,6 @@ constructor( private var _isNonProxy = BuildConfig.IS_NON_PROXY_APK private val fhirContext = FhirContext.forR4Cached() private val authConfiguration = configService.provideAuthConfiguration() - private val jsonParser = fhirContext.newJsonParser() /** * Retrieve configuration for the provided [ConfigType]. The JSON retrieved from [configsJsonMap] @@ -633,7 +632,7 @@ constructor( ) .apply { this.parentFile?.mkdirs() - writeText(jsonParser.encodeResourceToString(resource)) + writeText(fhirContext.newJsonParser().encodeResourceToString(resource)) } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/BaseP2PTransferDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/BaseP2PTransferDao.kt index 2893539540..75c9a9e156 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/BaseP2PTransferDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/BaseP2PTransferDao.kt @@ -16,8 +16,6 @@ package org.smartregister.fhircore.engine.p2p.dao -import ca.uhn.fhir.context.FhirContext -import ca.uhn.fhir.parser.IParser import ca.uhn.fhir.rest.gclient.DateClientParam import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum @@ -48,8 +46,6 @@ constructor( open val configurationRegistry: ConfigurationRegistry, ) { - protected val jsonParser: IParser = FhirContext.forR4Cached().newJsonParser() - open fun getDataTypes(): TreeSet { val appRegistry = configurationRegistry.retrieveConfiguration(ConfigType.Application) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/P2PReceiverTransferDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/P2PReceiverTransferDao.kt index 24270c0215..ddc1d1d759 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/P2PReceiverTransferDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/P2PReceiverTransferDao.kt @@ -16,6 +16,7 @@ package org.smartregister.fhircore.engine.p2p.dao +import ca.uhn.fhir.context.FhirContext import com.google.android.fhir.FhirEngine import com.google.android.fhir.datacapture.extensions.logicalId import java.util.TreeSet @@ -47,7 +48,9 @@ constructor( (0 until jsonArray.length()).forEach { runBlocking { val resource = - jsonParser.parseResource(type.name.resourceClassType(), jsonArray.get(it).toString()) + FhirContext.forR4Cached() + .newJsonParser() + .parseResource(type.name.resourceClassType(), jsonArray.get(it).toString()) val recordLastUpdated = resource.meta.lastUpdated.time defaultRepository.addOrUpdate(resource = resource) maxLastUpdated = diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/P2PSenderTransferDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/P2PSenderTransferDao.kt index f6a0c18123..ce537fb3b7 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/P2PSenderTransferDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/p2p/dao/P2PSenderTransferDao.kt @@ -16,6 +16,7 @@ package org.smartregister.fhircore.engine.p2p.dao +import ca.uhn.fhir.context.FhirContext import com.google.android.fhir.FhirEngine import com.google.android.fhir.datacapture.extensions.logicalId import java.util.TreeSet @@ -76,7 +77,7 @@ constructor( val jsonArray = JSONArray() records.forEach { - jsonArray.put(jsonParser.encodeResourceToString(it.resource)) + jsonArray.put(FhirContext.forR4Cached().newJsonParser().encodeResourceToString(it.resource)) highestRecordId = if (it.resource.meta?.lastUpdated?.time!! > highestRecordId) { it.resource.meta?.lastUpdated?.time!! diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/RulesFactory.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/RulesFactory.kt index 9e563579fe..c1da8bd196 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/RulesFactory.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/rulesengine/RulesFactory.kt @@ -147,8 +147,6 @@ constructor( /** Provide access to utility functions accessible to the users defining rules in JSON format. */ inner class RulesEngineService { - val parser = fhirContext.newJsonParser() - private var conf: Configuration = Configuration.defaultConfiguration().apply { addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL) } @@ -679,7 +677,9 @@ constructor( } val updatedResource = - parser.parseResource(resource::class.java, updatedResourceDocument.jsonString()) + fhirContext + .newJsonParser() + .parseResource(resource::class.java, updatedResourceDocument.jsonString()) CoroutineScope(dispatcherProvider.io()).launch { if (purgeAffectedResources) { defaultRepository.purge(updatedResource as Resource, forcePurge = true) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 91e1a40f90..04e898cc81 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -118,7 +118,6 @@ constructor( val configurationRegistry: ConfigurationRegistry, val knowledgeManager: KnowledgeManager, ) : ViewModel() { - private val parser = FhirContext.forR4Cached().newJsonParser() private val authenticatedOrganizationIds by lazy { sharedPreferencesHelper.read>(ResourceType.Organization.name) @@ -832,7 +831,9 @@ constructor( } private fun getStringRepresentation(base: Base): String = - if (base.isResource) parser.encodeResourceToString(base as Resource) else base.toString() + if (base.isResource) { + FhirContext.forR4Cached().newJsonParser().encodeResourceToString(base as Resource) + } else base.toString() /** * This function generates CarePlans for the [QuestionnaireResponse.subject] using the configured diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/sdk/CqlBuilder.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/sdk/CqlBuilder.kt index e193864813..28b710e0d7 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/sdk/CqlBuilder.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/sdk/CqlBuilder.kt @@ -16,7 +16,6 @@ package org.smartregister.fhircore.quest.sdk -import ca.uhn.fhir.context.FhirContext import java.io.InputStream import org.cqframework.cql.cql2elm.CqlTranslator import org.cqframework.cql.cql2elm.LibraryManager @@ -34,7 +33,6 @@ import org.junit.Assert.fail // required object CqlBuilder : Loadable() { - private val jsonParser = FhirContext.forR4Cached().newJsonParser() /** * Compiles a CQL Text to ELM From d2ffbb595b02dfbc9c2b76213358f51a9013da01 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 18 Sep 2024 14:12:25 +0300 Subject: [PATCH 100/110] Update FHIR SDK library versions --- .../geowidget/src/main/AndroidManifest.xml | 3 +-- android/gradle/libs.versions.toml | 20 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/android/geowidget/src/main/AndroidManifest.xml b/android/geowidget/src/main/AndroidManifest.xml index 738a9194e0..8d1ddfd243 100644 --- a/android/geowidget/src/main/AndroidManifest.xml +++ b/android/geowidget/src/main/AndroidManifest.xml @@ -3,8 +3,7 @@ + android:name="io.ona.kujaku.receivers.KujakuNetworkChangeReceiver"> diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 9d10da97b3..78a0c94a17 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -24,13 +24,13 @@ desugar-jdk-libs = "2.1.2" dokkaBase = "1.9.20" easy-rules-jexl = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" -fhir-sdk-common-utils = "1.0.0-SNAPSHOT" -fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" -fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview14.1-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview14.2-SNAPSHOT" -fhir-sdk-knowledge = "0.1.0-alpha03-preview5-SNAPSHOT" -fhir-sdk-workflow = "0.1.0-alpha04-preview10-SNAPSHOT" +common-utils = "1.0.0-SNAPSHOT" +fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-rc1-SNAPSHOT" +fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-rc1-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview14-rc1-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview14-rc1-SNAPSHOT" +fhir-sdk-knowledge = "0.1.0-alpha03-preview5-rc1-SNAPSHOT" +fhir-sdk-workflow = "0.1.0-alpha04-preview10-rc1-SNAPSHOT" fragment-ktx = "1.8.2" glide = "4.16.0" gradle = "8.3.2" @@ -50,8 +50,8 @@ kotlinx-coroutines = "1.8.1" kotlinx-serialization-json = "1.6.0" kt3k-coveralls-ver="2.12.0" ktlint = "0.50.0" -kujaku-library = "0.10.5-SNAPSHOT" -kujaku-mapbox-sdk-turf = "4.8.0" +kujaku-library = "0.10.6-SNAPSHOT" +kujaku-mapbox-sdk-turf = "7.2.0" leakcanary-android = "2.10" lifecycle= "2.8.5" logback-android = "3.0.0" @@ -121,7 +121,7 @@ datastore-preferences = { group = "androidx.datastore", name = "datastore-prefer dokka-base = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokkaBase" } easy-rules-jexl = { group = "org.smartregister", name = "easy-rules-jexl", version.ref = "easy-rules-jexl" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } -fhir-common-utils = { group = "org.smartregister", name = "fhir-common-utils", version.ref = "fhir-sdk-common-utils" } +fhir-common-utils = { group = "org.smartregister", name = "fhir-common-utils", version.ref = "common-utils" } fhir-engine = { group = "org.smartregister", name = "engine", version.ref = "fhir-sdk-engine" } foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "compose-ui" } fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment-ktx" } From 234c7d65e7f308ace0a5995f76b6a8379aa95f5e Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 18 Sep 2024 19:16:02 +0300 Subject: [PATCH 101/110] Geowidget configuration - Update Kujaku library version - Revert offline map downloads configuration via Kujaku network change receiver --- android/geowidget/src/main/AndroidManifest.xml | 7 +++++-- android/gradle/libs.versions.toml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/android/geowidget/src/main/AndroidManifest.xml b/android/geowidget/src/main/AndroidManifest.xml index 8d1ddfd243..2aa2993af7 100644 --- a/android/geowidget/src/main/AndroidManifest.xml +++ b/android/geowidget/src/main/AndroidManifest.xml @@ -1,9 +1,12 @@ - + + android:name="io.ona.kujaku.receivers.KujakuNetworkChangeReceiver" + android:exported="false" + tools:replace="exported"> diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 78a0c94a17..73616594ce 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -50,7 +50,7 @@ kotlinx-coroutines = "1.8.1" kotlinx-serialization-json = "1.6.0" kt3k-coveralls-ver="2.12.0" ktlint = "0.50.0" -kujaku-library = "0.10.6-SNAPSHOT" +kujaku-library = "0.10.6-2-SNAPSHOT" kujaku-mapbox-sdk-turf = "7.2.0" leakcanary-android = "2.10" lifecycle= "2.8.5" From 0a72665dd03fc6ad572798f20b7f981dee0dea72 Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Wed, 18 Sep 2024 20:17:45 +0300 Subject: [PATCH 102/110] =?UTF-8?q?Fix=20build=20=F0=9F=92=9A=20-=20Fix=20?= =?UTF-8?q?code=20coverage=20reporting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a259f1eb9..0ac130fc90 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: - name: Upload Engine module test coverage report to Codecov - if: matrix.api-level == 30 # Only upload coverage on API level 30 + if: matrix.api-level == 34 # Only upload coverage on API level 34 working-directory: android run: bash <(curl -s https://codecov.io/bash) -F engine -f "engine/build/reports/jacoco/fhircoreJacocoReport/fhircoreJacocoReport.xml" @@ -308,6 +308,6 @@ jobs: path: android/quest/build/reports - name: Upload Quest module test coverage report to Codecov - if: matrix.api-level == 30 # Only upload coverage on API level 30 + if: matrix.api-level == 34 # Only upload coverage on API level 34 working-directory: android run: bash <(curl -s https://codecov.io/bash) -F quest -f "quest/build/reports/jacoco/fhircoreJacocoReport/fhircoreJacocoReport.xml" From 7f4da95ab889799aeb0f44b6a9a7f65bd659548f Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 20 Sep 2024 15:38:53 +0300 Subject: [PATCH 103/110] SDK Engine to RC3 --- android/gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 73616594ce..cf8be9849c 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -28,7 +28,7 @@ common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-rc1-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-rc1-SNAPSHOT" fhir-sdk-data-capture = "1.1.0-preview14-rc1-SNAPSHOT" -fhir-sdk-engine = "1.0.0-preview14-rc1-SNAPSHOT" +fhir-sdk-engine = "1.0.0-preview14-rc3-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-alpha03-preview5-rc1-SNAPSHOT" fhir-sdk-workflow = "0.1.0-alpha04-preview10-rc1-SNAPSHOT" fragment-ktx = "1.8.2" From f694d0baa2112cfd110221456d44d0e02ebac31f Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Fri, 20 Sep 2024 16:32:35 +0300 Subject: [PATCH 104/110] Fix build --- .../fhircore/quest/pdf/PdfLauncherViewModelTest.kt | 1 - .../quest/ui/questionnaire/QuestionnaireViewModelTest.kt | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/pdf/PdfLauncherViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/pdf/PdfLauncherViewModelTest.kt index 0bba57aa0d..99370f41b6 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/pdf/PdfLauncherViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/pdf/PdfLauncherViewModelTest.kt @@ -48,7 +48,6 @@ class PdfLauncherViewModelTest : RobolectricTest() { defaultRepository = DefaultRepository( fhirEngine = fhirEngine, - dispatcherProvider = mockk(), sharedPreferencesHelper = mockk(), configurationRegistry = mockk(), configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 6b02918404..c28a0458db 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -1889,7 +1889,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { val questionnaireViewModelInstance = QuestionnaireViewModel( defaultRepository = defaultRepository, - dispatcherProvider = defaultRepository.dispatcherProvider, + dispatcherProvider = dispatcherProvider, fhirCarePlanGenerator = fhirCarePlanGenerator, resourceDataRulesExecutor = resourceDataRulesExecutor, transformSupportServices = mockk(), @@ -1898,6 +1898,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { fhirValidatorProvider = fhirValidatorProvider, fhirPathDataExtractor = fhirPathDataExtractor, configurationRegistry = configurationRegistry, + knowledgeManager = knowledgeManager, ) val questionnaireWithDefaultDate = Questionnaire().apply { From 8ad1cbdb8333f56ad1b6d469f17f0f02895d08ef Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:01:20 +0300 Subject: [PATCH 105/110] Fix failing ci tests --- .../fhircore/engine/data/local/DefaultRepositoryTest.kt | 6 ++++++ .../fhircore/geowidget/screens/GeoWidgetViewModelTest.kt | 1 + .../org/smartregister/fhircore/quest/integration/Faker.kt | 2 +- .../quest/data/report/measure/MeasureReportRepository.kt | 3 ++- .../quest/ui/questionnaire/QuestionnaireViewModel.kt | 1 + .../org/smartregister/fhircore/quest/ContentCacheTest.kt | 2 +- .../data/report/measure/MeasureReportPagingSourceTest.kt | 1 + .../fhircore/quest/ui/profile/ProfileViewModelTest.kt | 1 + .../quest/ui/questionnaire/QuestionnaireViewModelTest.kt | 1 + 9 files changed, 15 insertions(+), 3 deletions(-) diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt index 55d918e9f1..dc8eab48c1 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt @@ -98,6 +98,7 @@ import org.smartregister.fhircore.engine.domain.model.ResourceFilterExpression import org.smartregister.fhircore.engine.domain.model.RuleConfig import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor +import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.formatDate @@ -122,6 +123,8 @@ class DefaultRepositoryTest : RobolectricTest() { @Inject lateinit var parser: IParser + @Inject lateinit var dispatcherProvider: DispatcherProvider + @BindValue val configService: ConfigService = spyk(AppConfigService(ApplicationProvider.getApplicationContext())) @@ -145,6 +148,7 @@ class DefaultRepositoryTest : RobolectricTest() { fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = context, + dispatcherProvider = dispatcherProvider, ) } @@ -557,6 +561,7 @@ class DefaultRepositoryTest : RobolectricTest() { fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = context, + dispatcherProvider = dispatcherProvider, ), ) coEvery { fhirEngine.search(any()) } returns @@ -634,6 +639,7 @@ class DefaultRepositoryTest : RobolectricTest() { fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = context, + dispatcherProvider = dispatcherProvider, ), ) diff --git a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt index baaf1e49cd..60e3af783e 100644 --- a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt +++ b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetViewModelTest.kt @@ -97,6 +97,7 @@ class GeoWidgetViewModelTest { fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = ApplicationProvider.getApplicationContext(), + dispatcherProvider = dispatcherProvider, ), ) geoWidgetViewModel = spyk(GeoWidgetViewModel(dispatcherProvider)) diff --git a/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt b/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt index 014fb22ea4..01081c3ca8 100644 --- a/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt +++ b/android/quest/src/androidTest/java/org/smartregister/fhircore/quest/integration/Faker.kt @@ -112,7 +112,7 @@ object Faker { override suspend fun update(vararg resource: Resource) {} override suspend fun withTransaction(block: suspend CrudFhirEngine.() -> Unit) { - TODO("Not yet implemented") + TODO("Not yet implemented") } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt index 0c2cdeabf5..0c56bb0721 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportRepository.kt @@ -57,7 +57,7 @@ constructor( override val fhirPathDataExtractor: FhirPathDataExtractor, override val parser: IParser, @ApplicationContext override val context: Context, - val dispatcherProvider: DispatcherProvider, + override val dispatcherProvider: DispatcherProvider, ) : DefaultRepository( fhirEngine = fhirEngine, @@ -68,6 +68,7 @@ constructor( fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = context, + dispatcherProvider = dispatcherProvider, ) { /** diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index 82a53fcfda..d267912019 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -37,6 +37,7 @@ import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import dagger.hilt.android.lifecycle.HiltViewModel import java.util.Date +import java.util.LinkedList import java.util.UUID import javax.inject.Inject import javax.inject.Provider diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ContentCacheTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ContentCacheTest.kt index 5d2b18c0c3..4a94ca9f10 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ContentCacheTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ContentCacheTest.kt @@ -31,7 +31,7 @@ import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test -import org.smartregister.fhircore.quest.ui.questionnaire.ContentCache +import org.smartregister.fhircore.engine.datastore.ContentCache @OptIn(ExperimentalCoroutinesApi::class) class ContentCacheTest { diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt index b24e6d90ad..eb6c068f1c 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/data/report/measure/MeasureReportPagingSourceTest.kt @@ -114,6 +114,7 @@ class MeasureReportPagingSourceTest : RobolectricTest() { fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = ApplicationProvider.getApplicationContext(), + dispatcherProvider = dispatcherProvider, ), ) diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt index 4990d13671..408d98b3b0 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/profile/ProfileViewModelTest.kt @@ -105,6 +105,7 @@ class ProfileViewModelTest : RobolectricTest() { fhirPathDataExtractor = mockk(), parser = parser, context = ApplicationProvider.getApplicationContext(), + dispatcherProvider = dispatcherProvider, ), ) coEvery { diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 7b78a04a82..b7d4389d82 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -95,6 +95,7 @@ import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig import org.smartregister.fhircore.engine.configuration.UniqueIdAssignmentConfig import org.smartregister.fhircore.engine.configuration.app.ConfigService import org.smartregister.fhircore.engine.data.local.DefaultRepository +import org.smartregister.fhircore.engine.datastore.ContentCache import org.smartregister.fhircore.engine.domain.model.ActionParameter import org.smartregister.fhircore.engine.domain.model.ActionParameterType import org.smartregister.fhircore.engine.domain.model.QuestionnaireType From 7c517169fcc1d653afeb99cbe3913c79ad2bc44f Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:57:01 +0300 Subject: [PATCH 106/110] Update data-capture lib to v1.1.0-preview14-rc2-SNAPSHOT --- android/gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index ed4554b877..ce4ed96315 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -26,7 +26,7 @@ espresso-core = "3.6.1" common-utils = "1.0.0-SNAPSHOT" fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-rc1-SNAPSHOT" fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-rc1-SNAPSHOT" -fhir-sdk-data-capture = "1.1.0-preview14-rc1-SNAPSHOT" +fhir-sdk-data-capture = "1.1.0-preview14-rc2-SNAPSHOT" fhir-sdk-engine = "1.0.0-preview14-rc3-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-beta01-preview-rc1-SNAPSHOT" fhir-sdk-workflow = "0.1.0-alpha04-preview13-rc1-SNAPSHOT" From 2eb0b8c87c6ba97ac120ee3b9d9ba5299fcbed45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:07:52 +0300 Subject: [PATCH 107/110] Fix errors in tests --- .../engine/data/local/DefaultRepositoryTest.kt | 11 +++++------ .../data/local/register/RegisterRepositoryTest.kt | 4 ++++ .../fhircore/engine/task/FhirCarePlanGeneratorTest.kt | 2 +- .../engine/task/FhirResourceExpireWorkerTest.kt | 1 + .../smartregister/fhircore/quest/CqlContentTest.kt | 2 +- .../fhircore/quest/pdf/PdfLauncherViewModelTest.kt | 1 + .../ui/geowidget/GeoWidgetLauncherViewModelTest.kt | 4 ++++ 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt index 026ad892d4..2096a2e791 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt @@ -98,7 +98,7 @@ import org.smartregister.fhircore.engine.domain.model.ResourceFilterExpression import org.smartregister.fhircore.engine.domain.model.RuleConfig import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rulesengine.ConfigRulesExecutor -import org.smartregister.fhircore.engine.util.DispatcherProvider +import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.formatDate @@ -123,14 +123,13 @@ class DefaultRepositoryTest : RobolectricTest() { @Inject lateinit var parser: IParser - @Inject lateinit var dispatcherProvider: DispatcherProvider - @BindValue val configService: ConfigService = spyk(AppConfigService(ApplicationProvider.getApplicationContext())) private val application = ApplicationProvider.getApplicationContext() private val configurationRegistry: ConfigurationRegistry = Faker.buildTestConfigurationRegistry() private val context = ApplicationProvider.getApplicationContext() + private lateinit var dispatcherProvider: DefaultDispatcherProvider private lateinit var sharedPreferenceHelper: SharedPreferencesHelper private lateinit var defaultRepository: DefaultRepository @@ -142,6 +141,7 @@ class DefaultRepositoryTest : RobolectricTest() { defaultRepository = DefaultRepository( fhirEngine = fhirEngine, + dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferenceHelper, configurationRegistry = configurationRegistry, configService = configService, @@ -149,7 +149,6 @@ class DefaultRepositoryTest : RobolectricTest() { fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = context, - dispatcherProvider = dispatcherProvider, ) } @@ -555,6 +554,7 @@ class DefaultRepositoryTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, + dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = mockk(), configService = mockk(), @@ -562,7 +562,6 @@ class DefaultRepositoryTest : RobolectricTest() { fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = context, - dispatcherProvider = dispatcherProvider, ), ) coEvery { fhirEngine.search(any()) } returns @@ -633,6 +632,7 @@ class DefaultRepositoryTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, + dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = mockk(), configService = mockk(), @@ -640,7 +640,6 @@ class DefaultRepositoryTest : RobolectricTest() { fhirPathDataExtractor = fhirPathDataExtractor, parser = parser, context = context, - dispatcherProvider = dispatcherProvider, ), ) diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt index 448f29c2dc..142cebf0f6 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/register/RegisterRepositoryTest.kt @@ -67,6 +67,7 @@ import org.smartregister.fhircore.engine.domain.model.SyncLocationState import org.smartregister.fhircore.engine.robolectric.RobolectricTest import org.smartregister.fhircore.engine.rule.CoroutineTestRule import org.smartregister.fhircore.engine.rulesengine.RulesFactory +import org.smartregister.fhircore.engine.util.DispatcherProvider import org.smartregister.fhircore.engine.util.extension.asReference import org.smartregister.fhircore.engine.util.extension.encodeJson import org.smartregister.fhircore.engine.util.fhirpath.FhirPathDataExtractor @@ -98,6 +99,8 @@ class RegisterRepositoryTest : RobolectricTest() { @Inject lateinit var fhirPathDataExtractor: FhirPathDataExtractor + @Inject lateinit var dispatcherProvider: DispatcherProvider + @Inject lateinit var fhirEngine: FhirEngine @Inject lateinit var parser: IParser @@ -112,6 +115,7 @@ class RegisterRepositoryTest : RobolectricTest() { spyk( RegisterRepository( fhirEngine = fhirEngine, + dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt index c5a671dcd2..9f89f92c32 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirCarePlanGeneratorTest.kt @@ -2415,7 +2415,7 @@ class FhirCarePlanGeneratorTest : RobolectricTest() { } private suspend fun installToIgManager(resource: Resource) { - knowledgeManager.index(writeToFile(resource)) + knowledgeManager.install(writeToFile(resource)) } private suspend fun loadResource( diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt index 1490a719f6..c8594b2ed3 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/task/FhirResourceExpireWorkerTest.kt @@ -112,6 +112,7 @@ class FhirResourceExpireWorkerTest : RobolectricTest() { spyk( DefaultRepository( fhirEngine = fhirEngine, + dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = mockk(), configurationRegistry = configurationRegistry, configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt index 799cf84924..5b14d4dd22 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/CqlContentTest.kt @@ -197,7 +197,7 @@ class CqlContentTest : RobolectricTest() { private suspend fun createTestData(dataBundle: Bundle, cqlLibrary: Library) { dataBundle.entry.forEach { fhirEngine.create(it.resource) } - knowledgeManager.index( + knowledgeManager.install( File.createTempFile(cqlLibrary.name, ".json").apply { this.writeText(cqlLibrary.encodeResourceToString()) }, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/pdf/PdfLauncherViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/pdf/PdfLauncherViewModelTest.kt index 741a4226d8..6ea339a641 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/pdf/PdfLauncherViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/pdf/PdfLauncherViewModelTest.kt @@ -47,6 +47,7 @@ class PdfLauncherViewModelTest : RobolectricTest() { defaultRepository = DefaultRepository( fhirEngine = fhirEngine, + dispatcherProvider = mockk(), sharedPreferencesHelper = mockk(), configurationRegistry = mockk(), configService = mockk(), diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt index 4622eb84f7..dcfa53e5ea 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/geowidget/GeoWidgetLauncherViewModelTest.kt @@ -46,6 +46,7 @@ import org.smartregister.fhircore.engine.domain.model.FhirResourceConfig import org.smartregister.fhircore.engine.domain.model.ResourceConfig import org.smartregister.fhircore.engine.domain.model.SnackBarMessageConfig import org.smartregister.fhircore.engine.rulesengine.ResourceDataRulesExecutor +import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.quest.app.fakes.Faker import org.smartregister.fhircore.quest.robolectric.RobolectricTest @@ -58,6 +59,8 @@ class GeoWidgetLauncherViewModelTest : RobolectricTest() { @Inject lateinit var defaultRepository: DefaultRepository + @Inject lateinit var dispatcherProvider: DefaultDispatcherProvider + @Inject lateinit var sharedPreferencesHelper: SharedPreferencesHelper @Inject lateinit var resourceDataRulesExecutor: ResourceDataRulesExecutor @@ -98,6 +101,7 @@ class GeoWidgetLauncherViewModelTest : RobolectricTest() { viewModel = GeoWidgetLauncherViewModel( defaultRepository = defaultRepository, + dispatcherProvider = dispatcherProvider, sharedPreferencesHelper = sharedPreferencesHelper, resourceDataRulesExecutor = resourceDataRulesExecutor, configurationRegistry = configurationRegistry, From 692380dafb07e412acd2c8a719c2a6878582b2c8 Mon Sep 17 00:00:00 2001 From: Benjamin Mwalimu Date: Tue, 5 Nov 2024 12:14:50 +0300 Subject: [PATCH 108/110] =?UTF-8?q?=F0=9F=90=9B=20Readd=20the=20eusm=20mg?= =?UTF-8?q?=20and=20bi=20flavour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/engine/lint-baseline.xml | 1518 ++++++++++++++++++++++++++++++ android/quest/build.gradle.kts | 16 +- 2 files changed, 1530 insertions(+), 4 deletions(-) create mode 100644 android/engine/lint-baseline.xml diff --git a/android/engine/lint-baseline.xml b/android/engine/lint-baseline.xml new file mode 100644 index 0000000000..f03fbd95a3 --- /dev/null +++ b/android/engine/lint-baseline.xml @@ -0,0 +1,1518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/quest/build.gradle.kts b/android/quest/build.gradle.kts index 460a8b93cd..ecb8f05d7e 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -321,13 +321,21 @@ android { manifestPlaceholders["appLabel"] = "PSI WFA" } - create("eusm") { + create("eusmMg") { dimension = "apps" - applicationIdSuffix = ".eusm" - versionNameSuffix = "-eusm" - manifestPlaceholders["appLabel"] = "EUSM" + applicationIdSuffix = ".eusmMg" + versionNameSuffix = "-eusmMg" + manifestPlaceholders["appLabel"] = "EUSM Madagascar" } + create("eusmBi") { + dimension = "apps" + applicationIdSuffix = ".eusmBi" + versionNameSuffix = "-eusmBi" + manifestPlaceholders["appLabel"] = "EUSM Burundi" + } + + create("demoEir") { dimension = "apps" applicationIdSuffix = ".demoEir" From 699328932f0daa64ad3f50a247e2ca770f17aa23 Mon Sep 17 00:00:00 2001 From: Benjamin Mwalimu Date: Tue, 5 Nov 2024 12:16:15 +0300 Subject: [PATCH 109/110] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unnecessary=20f?= =?UTF-8?q?iles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/engine/lint-baseline.xml | 1518 ------------------------------ 1 file changed, 1518 deletions(-) delete mode 100644 android/engine/lint-baseline.xml diff --git a/android/engine/lint-baseline.xml b/android/engine/lint-baseline.xml deleted file mode 100644 index f03fbd95a3..0000000000 --- a/android/engine/lint-baseline.xml +++ /dev/null @@ -1,1518 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From b888647ae6682dc1a3be4e4630775c087ee9d307 Mon Sep 17 00:00:00 2001 From: Benjamin Mwalimu Date: Tue, 5 Nov 2024 12:18:20 +0300 Subject: [PATCH 110/110] =?UTF-8?q?=F0=9F=8D=BB=20Removing=20double=20flav?= =?UTF-8?q?our=20defination?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/quest/build.gradle.kts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/android/quest/build.gradle.kts b/android/quest/build.gradle.kts index a14601220c..b7855080e3 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -335,14 +335,6 @@ android { manifestPlaceholders["appLabel"] = "EUSM Burundi" } - create("eusmBi") { - dimension = "apps" - applicationIdSuffix = ".eusmBi" - versionNameSuffix = "-eusmBi" - manifestPlaceholders["appLabel"] = "EUSM Burundi" - } - - create("demoEir") { dimension = "apps" applicationIdSuffix = ".demoEir"