From 25f14834c95d63f3c77cf6246a6dc16138aa0f21 Mon Sep 17 00:00:00 2001 From: sharon2719 Date: Mon, 9 Dec 2024 16:53:39 +0300 Subject: [PATCH] Update Composition.focus to handle Composition.entry --- .../configuration/ConfigurationRegistry.kt | 144 +++++++++++----- .../configs/app/composition_config.json | 162 +++++++++++++++--- .../configs/app/composition_config.json | 120 +++++++++++-- 3 files changed, 346 insertions(+), 80 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 50579cce38..f135205f6a 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 @@ -265,16 +265,18 @@ constructor( addOrUpdate(localCompositionResource) localCompositionResource.run { - val iconConfigs = - retrieveCompositionSections().filter { - it.focus.hasIdentifier() && isIconConfig(it.focus.identifier.value) - } - if (iconConfigs.isNotEmpty()) { - val ids = iconConfigs.joinToString(DEFAULT_STRING_SEPARATOR) { it.focus.extractId() } + val configsToProcess = + retrieveCompositionSections() + .flatMap { section -> + section.entry.ifEmpty { listOfNotNull(section.focus.takeIf { it.hasIdentifier() }) } + } + .filter { it.hasReferenceElement() && it.hasIdentifier() } + + val ids = configsToProcess.joinToString(DEFAULT_STRING_SEPARATOR) { it.extractId() } + + if (ids.isNotEmpty()) { fhirResourceDataSource - .getResource( - "${ResourceType.Binary.name}?$ID=$ids&_count=$DEFAULT_COUNT", - ) + .getResource("${ResourceType.Binary.name}?$ID=$ids&_count=$DEFAULT_COUNT") .entry .forEach { addOrUpdate(it.resource) } } @@ -337,7 +339,26 @@ constructor( } } } else { + // Process composition sections composition.retrieveCompositionSections().forEach { + // Check .entry first + it.entry.forEach { entry -> + if (entry.hasReferenceElement() && entry.hasIdentifier()) { + val configIdentifier = entry.identifier.value + val referenceResourceType = entry.reference.substringBefore(TYPE_REFERENCE_DELIMITER) + if (isAppConfig(referenceResourceType) && !isIconConfig(configIdentifier)) { + val extractedId = entry.extractId() + try { + val configBinary = fhirEngine.get(extractedId) + configsJsonMap[configIdentifier] = configBinary.content.decodeToString() + } catch (resourceNotFoundException: ResourceNotFoundException) { + Timber.e("Missing Binary file with ID :$extractedId") + withContext(dispatcherProvider.main()) { configsLoadedCallback(false) } + } + } + } + } + // If .entry doesn't provide the necessary data, fallback to .focus if (it.hasFocus() && it.focus.hasReferenceElement() && it.focus.hasIdentifier()) { val configIdentifier = it.focus.identifier.value val referenceResourceType = it.focus.reference.substringBefore(TYPE_REFERENCE_DELIMITER) @@ -439,13 +460,30 @@ constructor( ) }", ) - fetchResources( - resourceType = entry.key, - resourceIdList = - sectionComponents.map { sectionComponent -> - sectionComponent.focus.extractId() - }, - ) + + // Check .entry first, fallback to .focus if necessary + sectionComponents.forEach { sectionComponent -> + // Prioritize .entry for resource extraction + sectionComponent.entry.forEach { entry -> + if (entry.hasReferenceElement() && entry.hasIdentifier()) { + val resourceId = entry.extractId() + fetchResources( + resourceType = entry.reference.substringBefore(TYPE_REFERENCE_DELIMITER), + resourceIdList = listOf(resourceId), + ) + } + } + + // Fallback to .focus if .entry doesn't provide valid reference or identifier + if (sectionComponent.hasFocus() && sectionComponent.focus.hasReferenceElement()) { + val resourceId = sectionComponent.focus.extractId() + fetchResources( + resourceType = + sectionComponent.focus.reference.substringBefore(TYPE_REFERENCE_DELIMITER), + resourceIdList = listOf(resourceId), + ) + } + } } } } @@ -686,37 +724,61 @@ constructor( private suspend fun processCompositionListResources( sectionComponentEntry: Map.Entry>, ) { + val resourceType = sectionComponentEntry.key + val sectionComponents = sectionComponentEntry.value + if (isNonProxy()) { - val chunkedResourceIdList = sectionComponentEntry.value.chunked(MANIFEST_PROCESSOR_BATCH_SIZE) - chunkedResourceIdList.forEach { - fetchResources( - resourceType = sectionComponentEntry.key, - resourceIdList = it.map { sectionComponent -> sectionComponent.focus.extractId() }, - ) - .entry - .forEach { bundleEntryComponent -> - when (bundleEntryComponent.resource) { - is ListResource -> { - addOrUpdate(bundleEntryComponent.resource) - val list = bundleEntryComponent.resource as ListResource - list.entry.forEach { listEntryComponent -> - val resourceKey = - listEntryComponent.item.reference.substringBefore(TYPE_REFERENCE_DELIMITER) - val resourceId = listEntryComponent.item.reference.extractLogicalIdUuid() - val listResourceUrlPath = "$resourceKey?$ID=$resourceId&_count=$DEFAULT_COUNT" - fetchResources(gatewayModeHeaderValue = null, url = listResourceUrlPath) - } + val chunkedSections = sectionComponents.chunked(MANIFEST_PROCESSOR_BATCH_SIZE) + chunkedSections.forEach { chunk -> + try { + val resourceIds = + chunk + .flatMap { section -> + // Include IDs from .entry and .focus + section.entry.mapNotNull { it.extractId() } + + listOfNotNull(section.focus.extractId()) } + .distinct() + + val fetchedResources = + fetchResources(resourceType = resourceType, resourceIdList = resourceIds) + + fetchedResources.entry.forEach { bundleEntry -> + when (val resource = bundleEntry.resource) { + is ListResource -> processListResource(resource) } } + } catch (exception: Exception) { + Timber.e("Error processing non-proxy resources for type $resourceType: $exception") + } } } else { - sectionComponentEntry.value.forEach { - fetchResources( - gatewayModeHeaderValue = FHIR_GATEWAY_MODE_HEADER_VALUE, - url = - "${sectionComponentEntry.key}?$ID=${it.focus.extractId()}&_page=1&_count=$DEFAULT_COUNT", - ) + sectionComponents.forEach { section -> + try { + // Process .entry and .focus for gateway mode + val resourceIds = + section.entry.mapNotNull { it.extractId() } + listOfNotNull(section.focus.extractId()) + resourceIds.forEach { resourceId -> + val url = "$resourceType?$ID=$resourceId&_page=1&_count=$DEFAULT_COUNT" + fetchResources(gatewayModeHeaderValue = FHIR_GATEWAY_MODE_HEADER_VALUE, url = url) + } + } catch (exception: Exception) { + Timber.e("Error processing proxy resources for type $resourceType: $exception") + } + } + } + } + + private suspend fun processListResource(listResource: ListResource) { + addOrUpdate(listResource) + listResource.entry.forEach { listEntry -> + val reference = listEntry.item.reference + val resourceKey = reference.substringBefore(TYPE_REFERENCE_DELIMITER) + val resourceId = reference.extractLogicalIdUuid() + + if (resourceKey.isNotEmpty() && resourceId.isNotEmpty()) { + val listResourceUrlPath = "$resourceKey?$ID=$resourceId&_count=$DEFAULT_COUNT" + fetchResources(gatewayModeHeaderValue = null, url = listResourceUrlPath) } } } diff --git a/android/quest/src/main/assets/configs/app/composition_config.json b/android/quest/src/main/assets/configs/app/composition_config.json index a8c3e167cd..46b153ca3a 100644 --- a/android/quest/src/main/assets/configs/app/composition_config.json +++ b/android/quest/src/main/assets/configs/app/composition_config.json @@ -14,19 +14,39 @@ "type": { "coding": [ { - "system": "http://snomed.info/sct", - "code": "1156600005", + "system": "https://loinc.org", + "code": "11503-0", "display": "Device setting parameter" } ] }, + "category": [ + { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "1156600005", + "display": "Device setting parameter" + } + ] + } + ], + "author": [ + { + "reference": "Practitioner/76f11bf7-0323-429f-bc7c-481f2f29fc51" + } + ], "date": "2022-09-16", "title": "Device configurations", "confidentiality": "L", "section": [ { "title": "Application configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Application configuration details
" + }, + "entry": { "reference": "Binary/4755546c-e61f-43bb-b599-5cad230a3d529e", "identifier": { "value": "application" @@ -36,7 +56,11 @@ }, { "title": "Sync configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Synchronization settings and details
" + }, + "entry": { "reference": "Binary/a982504f-8d8b-4151-abc6-0063793500d4e", "identifier": { "value": "sync" @@ -46,7 +70,11 @@ }, { "title": "Navigation configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Navigation settings and map configurations
" + }, + "entry": { "reference": "Binary/d7ce0167-ee6a-4f8f-b644-50b0242513239e", "identifier": { "value": "navigation" @@ -56,11 +84,19 @@ }, { "title": "Register configurations", + "text": { + "status": "generated", + "div": "
Register configuration
" + }, "mode": "working", "section": [ { "title": "Household register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Household register configuration
" + }, + "entry": { "reference": "Binary/7ebbbf4e-c783-42f2-998d-3c6bdde5c0c6e", "identifier": { "value": "householdRegister" @@ -69,7 +105,11 @@ }, { "title": "ANC register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
ANC register configuration
" + }, + "entry": { "reference": "Binary/e64815e6-56c1-4200-b2a6-6ec13eedfec3e", "identifier": { "value": "ancRegister" @@ -78,7 +118,11 @@ }, { "title": "Child register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Child register configuration
" + }, + "entry": { "reference": "Binary/7a68f458-9402-4c07-be62-feb85d341c17e", "identifier": { "value": "childRegister" @@ -87,7 +131,11 @@ }, { "title": "Task register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Task register configuration
" + }, + "entry": { "reference": "Binary/bbf28ade-5bc5-11ed-9b6a-0242ac120090e", "identifier": { "value": "taskRegister" @@ -96,7 +144,11 @@ }, { "title": "CMNTD register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
CMNTD register configuration
" + }, + "entry": { "reference": "Binary/d90ae75b-b180-45c0-ab57-24dc6c885cc1e", "identifier": { "value": "cmntdRegister" @@ -105,7 +157,11 @@ }, { "title": "FP register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
FP register configuration
" + }, + "entry": { "reference": "Binary/7d0ce69f-22c2-4829-90b4-c0aadebdffbae", "identifier": { "value": "fpRegister" @@ -114,7 +170,11 @@ }, { "title": "HIV register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
HIV register configuration
" + }, + "entry": { "reference": "Binary/faa1688d-98fa-4954-9889-6c2bdf4d4e57e", "identifier": { "value": "hivRegister" @@ -123,7 +183,11 @@ }, { "title": "Mental Health register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Mental Health register configuration
" + }, + "entry": { "reference": "Binary/ba22490f-d24b-43d0-b6f4-f5af474bbf05e", "identifier": { "value": "mentalHealthRegister" @@ -132,7 +196,11 @@ }, { "title": "PNC register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
PNC register configuration
" + }, + "entry": { "reference": "Binary/9aa6bbb6-df76-42a4-bdbe-72dc197378cae", "identifier": { "value": "pncRegister" @@ -141,7 +209,11 @@ }, { "title": "Sick Child register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Sick Child register configuration
" + }, + "entry": { "reference": "Binary/079a2120-e802-4445-95ac-59511751a4c0e", "identifier": { "value": "sickChildRegister" @@ -150,7 +222,11 @@ }, { "title": "TB register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
TB register configuration
" + }, + "entry": { "reference": "Binary/2ac6ec4f-1a29-4d85-a015-06db9c1b82d9e", "identifier": { "value": "tbRegister" @@ -161,11 +237,19 @@ }, { "title": "Profile configurations", + "text": { + "status": "generated", + "div": "
Profile configurations
" + }, "mode": "working", "section": [ { "title": "Household profile configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Household Profile configurations
" + }, + "entry": { "reference": "Binary/bbb3c2b0-51c9-44e9-9674-7d311ad5f859e", "identifier": { "value": "householdProfile" @@ -174,7 +258,11 @@ }, { "title": "Default profile configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Default profile configuration
" + }, + "entry": { "reference": "Binary/34b709f3-e8a1-44e9-867a-714b68bb1367e", "identifier": { "value": "defaultProfile" @@ -183,7 +271,11 @@ }, { "title": "Other Registers profile configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Other registers profile configuration
" + }, + "entry": { "reference": "Binary/a6d69650-4806-4bb1-bd2b-a60fa18418a0e", "identifier": { "value": "otherRegistersProfile" @@ -194,11 +286,19 @@ }, { "title": "Translation configurations", + "text": { + "status": "generated", + "div": "
Translation configurations
" + }, "mode": "working", "section": [ { "title": "Translation English", - "focus": { + "text": { + "status": "generated", + "div": "
Translation English
" + }, + "entry": { "reference": "Binary/c6757818-25c8-4e51-ba51-0c9dd882cbbbe", "identifier": { "value": "strings" @@ -207,7 +307,11 @@ }, { "title": "Translation Swahili", - "focus": { + "text": { + "status": "generated", + "div": "
Translation Swahili
" + }, + "entry": { "reference": "Binary/eee5f9c1-49f6-4b18-b088-8a52689a79d8e", "identifier": { "value": "strings_sw" @@ -216,7 +320,11 @@ }, { "title": "Translation French", - "focus": { + "text": { + "status": "generated", + "div": "
Translation French
" + }, + "entry": { "reference": "Binary/43e6c036-bb83-481e-8ffd-f8fdb7ee54a0e", "identifier": { "value": "strings_fr" @@ -227,11 +335,19 @@ }, { "title": "Questionnaires", + "text": { + "status": "generated", + "div": "
Questionnaires
" + }, "mode": "working", "section": [ { "title": "Add household", - "focus": { + "text": { + "status": "generated", + "div": "
Add household questionnaire
" + }, + "entry": { "reference": "Questionnaire/f210a832-857f-49e6-93f5-399eec4f4edb", "identifier": { "value": "household-registration" diff --git a/android/quest/src/test/assets/configs/app/composition_config.json b/android/quest/src/test/assets/configs/app/composition_config.json index a53bd7725e..ff76529c87 100644 --- a/android/quest/src/test/assets/configs/app/composition_config.json +++ b/android/quest/src/test/assets/configs/app/composition_config.json @@ -14,19 +14,39 @@ "type": { "coding": [ { - "system": "http://snomed.info/sct", - "code": "1156600005", + "system": "http://loinc.org", + "code": "11503-0", "display": "Device setting parameter" } ] }, + "category": [ + { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "1156600005", + "display": "Device setting parameter" + } + ] + } + ], + "author": [ + { + "reference": "Practitioner/76f11bf7-0323-429f-bc7c-481f2f29fc51" + } + ], "date": "2022-07-28", "title": "Device configurations", "confidentiality": "L", "section": [ { "title": "Application configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Application configuration details
" + }, + "entry": { "reference": "Binary/11111111", "identifier": { "value": "application" @@ -36,7 +56,11 @@ }, { "title": "Sync configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Sync configuration
" + }, + "entry": { "reference": "Binary/11111112", "identifier": { "value": "sync" @@ -46,7 +70,11 @@ }, { "title": "Navigation configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Navigation configuration
" + }, + "entry": { "reference": "Binary/11111114", "identifier": { "value": "navigation" @@ -56,11 +84,19 @@ }, { "title": "Register configurations", + "text": { + "status": "generated", + "div": "
Register configurations
" + }, "mode": "working", "section": [ { "title": "Household register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Household Configuration
" + }, + "entry": { "reference": "Binary/11111115", "identifier": { "value": "householdRegister" @@ -69,7 +105,11 @@ }, { "title": "Task register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Task configuration
" + }, + "entry": { "reference": "Binary/11111116", "identifier": { "value": "taskRegister" @@ -78,7 +118,11 @@ }, { "title": "ANC register configuration", - "focus": { + "text": { + "status": "generated", + "div": "
ANC register configuration
" + }, + "entry": { "reference": "Binary/11111117", "identifier": { "value": "ancRegister" @@ -89,11 +133,19 @@ }, { "title": "Translation configurations", + "text": { + "status": "generated", + "div": "
Translation configuration
" + }, "mode": "working", "section": [ { "title": "Translation English", - "focus": { + "text": { + "status": "generated", + "div": "
Translation English
" + }, + "entry": { "reference": "Binary/21111111", "identifier": { "value": "strings" @@ -102,7 +154,11 @@ }, { "title": "Translation Swahili", - "focus": { + "text": { + "status": "generated", + "div": "
Translation Swahili
" + }, + "entry": { "reference": "Binary/21111112", "identifier": { "value": "strings_sw" @@ -111,7 +167,11 @@ }, { "title": "Translation French", - "focus": { + "text": { + "status": "generated", + "div": "
Translation French
" + }, + "entry": { "reference": "Binary/21111113", "identifier": { "value": "strings_fr" @@ -122,7 +182,11 @@ }, { "title": "Measure reports configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Measure reports configuration
" + }, + "entry": { "reference": "Binary/11111118", "identifier": { "value": "measureReport" @@ -132,11 +196,19 @@ }, { "title": "Profile configurations", + "text": { + "status": "generated", + "div": "
Profile configurations
" + }, "mode": "working", "section": [ { "title": "Household profile configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Household profile configuration
" + }, + "entry": { "reference": "Binary/11111119", "identifier": { "value": "householdProfile" @@ -145,7 +217,11 @@ }, { "title": "Default profile configuration", - "focus": { + "text": { + "status": "generated", + "div": "
Default profile configuration
" + }, + "entry": { "reference": "Binary/11111120", "identifier": { "value": "defaultProfile" @@ -156,12 +232,20 @@ }, { "title": "Icon configurations", + "text": { + "status": "generated", + "div": "
Icon configurations
" + }, "mode": "working", "section": [ { "title": "Icon households", + "text": { + "status": "generated", + "div": "
Icon households
" + }, "mode": "working", - "focus": { + "entry": { "reference": "Binary/144177", "identifier": { "value": "ic_households" @@ -170,8 +254,12 @@ }, { "title": "Icon pregnant", + "text": { + "status": "generated", + "div": "
Icon pregnant
" + }, "mode": "working", - "focus": { + "entry": { "reference": "Binary/145193", "identifier": { "value": "ic_pregnant"