Skip to content

Commit

Permalink
Fix loading related resources data for LIST (#3539)
Browse files Browse the repository at this point in the history
* Include listRepositoryResourceDataMap to nested list widgets

Signed-off-by: Elly Kitoto <[email protected]>

* Fix loading related resources data for List widget

Signed-off-by: Elly Kitoto <[email protected]>

* Refactor loading bitmap images (#3546)

* Add date service to rules engine facts map (#3519)

* Add summary mode (#3500)

Co-authored-by: Elly Kitoto <[email protected]>

* Refactor PDF config properties in QuestionnaireConfig (#3498)

* Refactor to use PdfConfig

Initially using QuestionnaireConfig for simplicity.

* Process multi QRs in HtmlPopulator

* Add new tag to check if Questionnaire has been submitted

* Remove subjectType since subjectReference is used

* Fix test and spotless

* Address review

* spotless

* Cleanup

* spotless

* Fix test

---------

Co-authored-by: Elly Kitoto <[email protected]>

* Fix error validation highlighting not working when submit button is clicked bug (#3525)

* Change progress bar color to blue (#3428)

* update in-progress color on the map view. (#3529)

      Signed-off-by: Lentumunai-Mark <[email protected]>

* Add medication sort custom search param (#3534)

* Add medication sort custom search param

* Run spotlessApply

* Fix failing time based test

* Pld docs adjustment (#3540)

* sync typo

* correct heading title

* update header definition for p2p

* upgrade packages

* Update Geowidget to only show the Set Location dialog if no Locations to sync by have been selected (#3526)

* Don't show no results dialogue on map when locations selected on multiselect.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Fix failing tests.

Signed-off-by: Lentumunai-Mark <[email protected]>

---------

Signed-off-by: Lentumunai-Mark <[email protected]>

* Add Exit Dialog (#3487)

* Add exit dialog

* Fix a typo

* Show Dialog only when Map or Register is visible

* PR feedback changes

* Fix failing check

* Remove incorrect test

* Refactor dialog condition logic

---------

Co-authored-by: Benjamin Mwalimu <[email protected]>

* Refactor working with bitmap images

Signed-off-by: Elly Kitoto <[email protected]>

* Add the EUSM Burundi flavour and Rename the existing EUSM flavour (#3548)

* - Add the EUSM Burundi flavour
- Rename the exisiting EUSM flavour

* - Update the app naming

* - update the flavour naming

* Configure submit anyway button (#3535)

* Update the Burundi EUSM flavor (#3553)

* - Add the EUSM Burundi flavour
- Rename the exisiting EUSM flavour

* - Update the app naming

* - update the flavour naming

* - Update the flavor naming
- Update the documentation

* - Update the flavors section of the documentation to clarify on the naming convention

* Bum p up data capture version (#3547)

* Map search bug fixes (#3516)

* - Updating the infinite scroll paging
- Updating the behaviour after clearing search bar on the maps

* - spotless apply

* - Fix breaking tests on geo widget module

* - Fix tests

* - Run spotless

* - Import `LazyPagingItems`

* - Adding tests

* - Update the APK naming

* Ignore test to be refactored later

Signed-off-by: Elly Kitoto <[email protected]>

* - Run spotless

* Update kujaku version.

Signed-off-by: Lentumunai-Mark <[email protected]>

---------

Signed-off-by: Elly Kitoto <[email protected]>
Signed-off-by: Lentumunai-Mark <[email protected]>
Co-authored-by: Lentumunai Mark <[email protected]>
Co-authored-by: Elly Kitoto <[email protected]>
Co-authored-by: Lentumunai-Mark <[email protected]>

* Fix loading related resources

Signed-off-by: Elly Kitoto <[email protected]>

---------

Signed-off-by: Lentumunai-Mark <[email protected]>
Signed-off-by: Elly Kitoto <[email protected]>
Co-authored-by: Rkareko <[email protected]>
Co-authored-by: FikriMilano <[email protected]>
Co-authored-by: Lentumunai Mark <[email protected]>
Co-authored-by: Peter Lubell-Doughtie <[email protected]>
Co-authored-by: Hamza Ahmed Khan <[email protected]>
Co-authored-by: Benjamin Mwalimu <[email protected]>
Co-authored-by: Lentumunai-Mark <[email protected]>

* Fix failing UI tests

Signed-off-by: Elly Kitoto <[email protected]>

* Interpolate conditionalFhirPathExpressions in listResource (#3538)

To pass in evaluated rule variables

Signed-off-by: Elly Kitoto <[email protected]>
Co-authored-by: Elly Kitoto <[email protected]>

* Fix apply stack tint.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Apply opacity on view properties. (#3564)

Signed-off-by: Lentumunai-Mark <[email protected]>

* Fix duplication of List view item

Signed-off-by: Elly Kitoto <[email protected]>

---------

Signed-off-by: Elly Kitoto <[email protected]>
Signed-off-by: Lentumunai-Mark <[email protected]>
Co-authored-by: Rkareko <[email protected]>
Co-authored-by: FikriMilano <[email protected]>
Co-authored-by: Lentumunai Mark <[email protected]>
Co-authored-by: Peter Lubell-Doughtie <[email protected]>
Co-authored-by: Hamza Ahmed Khan <[email protected]>
Co-authored-by: Benjamin Mwalimu <[email protected]>
Co-authored-by: Lentumunai-Mark <[email protected]>
Co-authored-by: L≡ZRS <[email protected]>
  • Loading branch information
9 people authored Oct 18, 2024
1 parent 2fe2a47 commit 8f2c558
Show file tree
Hide file tree
Showing 61 changed files with 532 additions and 370 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ package org.smartregister.fhircore.engine.configuration

import android.content.Context
import android.database.SQLException
import android.graphics.Bitmap
import androidx.compose.runtime.mutableStateMapOf
import ca.uhn.fhir.context.ConfigurationException
import ca.uhn.fhir.context.FhirContext
import ca.uhn.fhir.parser.DataFormatException
Expand Down Expand Up @@ -98,7 +96,6 @@ constructor(

val configsJsonMap = mutableMapOf<String, String>()
val configCacheMap = mutableMapOf<String, Configuration>()
val decodedImageMap = mutableStateMapOf<String, Bitmap>()
val localizationHelper: LocalizationHelper by lazy { LocalizationHelper(this) }
private val supportedFileExtensions = listOf("json", "properties")
private var _isNonProxy = BuildConfig.IS_NON_PROXY_APK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ data class ButtonProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "false",
override val visible: String = "true",
override val opacity: Float? = null,
val contentColor: String? = null,
val enabled: String = "true",
val text: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ data class CardViewProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "true",
override val visible: String = "true",
override val opacity: Float? = null,
val content: List<ViewProperties> = emptyList(),
val elevation: Int = 5,
val cornerSize: Int = 6,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ data class ColumnProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "false",
override val visible: String = "true",
override val opacity: Float? = null,
val spacedBy: Int = 8,
val wrapContent: Boolean = false,
val arrangement: ColumnArrangement? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ data class CompoundTextProperties(
override val alignment: ViewAlignment = ViewAlignment.NONE,
override val fillMaxWidth: Boolean = false,
override val fillMaxHeight: Boolean = false,
override val opacity: Float? = null,
override val clickable: String = "false",
override val visible: String = "true",
val primaryText: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ data class DividerProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "false",
override val visible: String = "true",
override val opacity: Float? = null,
val thickness: Float = 0.5f,
) : ViewProperties(), Parcelable {
override fun interpolate(computedValuesMap: Map<String, Any>): DividerProperties {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ data class ImageProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "false",
override val visible: String = "true",
override val opacity: Float? = null,
val tint: String? = null,
val text: String? = null,
val imageConfig: ImageConfig? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ data class ListProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "false",
override val visible: String = "true",
override val opacity: Float? = null,
val id: String = "listId",
val registerCard: RegisterCardConfig,
val showDivider: Boolean = true,
val emptyList: NoResultsConfig? = null,
val orientation: ListOrientation = ListOrientation.VERTICAL,
val resources: List<ListResource> = emptyList(),
val resources: List<ListResourceConfig> = emptyList(),
) : ViewProperties(), Parcelable {
override fun interpolate(computedValuesMap: Map<String, Any>): ListProperties {
return this.copy(
Expand All @@ -61,12 +62,13 @@ enum class ListOrientation {

@Serializable
@Parcelize
data class ListResource(
data class ListResourceConfig(
val id: String? = null,
val relatedResourceId: String? = null,
val resourceType: ResourceType,
val conditionalFhirPathExpression: String? = null,
val sortConfig: SortConfig? = null,
val fhirPathExpression: String? = null,
val relatedResources: List<ListResource> = emptyList(),
val relatedResources: List<ListResourceConfig> = emptyList(),
val isRevInclude: Boolean = true,
) : Parcelable, java.io.Serializable
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ data class PersonalDataProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "false",
override val visible: String = "true",
override val opacity: Float? = null,
val personalDataItems: List<PersonalDataItem> = emptyList(),
) : ViewProperties(), Parcelable {
override fun interpolate(computedValuesMap: Map<String, Any>): PersonalDataProperties {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ data class RowProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "false",
override val visible: String = "true",
override val opacity: Float? = null,
val spacedBy: Int = 8,
val arrangement: RowArrangement? = null,
val wrapContent: Boolean = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ data class ServiceCardProperties(
override val fillMaxHeight: Boolean = false,
override val clickable: String = "true",
override val visible: String = "true",
override val opacity: Float? = null,
val details: List<CompoundTextProperties> = emptyList(),
val showVerticalDivider: Boolean = false,
val serviceMemberIcons: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ data class SpacerProperties(
override val alignment: ViewAlignment = ViewAlignment.NONE,
override val fillMaxWidth: Boolean = false,
override val fillMaxHeight: Boolean = false,
override val opacity: Float? = null,
override val clickable: String = "false",
override val visible: String = "true",
val height: Float? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ import org.smartregister.fhircore.engine.util.extension.interpolate
data class StackViewProperties(
override val viewType: ViewType = ViewType.STACK,
override val weight: Float = 0f,
override val backgroundColor: String? = "#FFFFFF",
override val backgroundColor: String? = null,
override val padding: Int = 0,
override val borderRadius: Int = 0,
override val alignment: ViewAlignment = ViewAlignment.NONE,
override val fillMaxWidth: Boolean = false,
override val fillMaxHeight: Boolean = false,
override val clickable: String = "false",
override val visible: String = "true",
val opacity: Float = 0f,
val size: Int? = 0,
override val opacity: Float? = null,
val size: Int = 0,
val children: List<ViewProperties> = emptyList(),
) : ViewProperties(), Parcelable {
override fun interpolate(computedValuesMap: Map<String, Any>): StackViewProperties {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ abstract class ViewProperties : java.io.Serializable {
abstract val fillMaxHeight: Boolean
abstract val clickable: String
abstract val visible: String
abstract val opacity: Float?

abstract fun interpolate(computedValuesMap: Map<String, Any>): ViewProperties
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ import javax.inject.Inject
import org.hl7.fhir.r4.model.Resource
import org.jeasy.rules.api.Facts
import org.smartregister.fhircore.engine.configuration.view.ListProperties
import org.smartregister.fhircore.engine.configuration.view.ListResource
import org.smartregister.fhircore.engine.configuration.view.ListResourceConfig
import org.smartregister.fhircore.engine.domain.model.RepositoryResourceData
import org.smartregister.fhircore.engine.domain.model.ResourceData
import org.smartregister.fhircore.engine.domain.model.RuleConfig
import org.smartregister.fhircore.engine.domain.model.ViewType
import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid
import org.smartregister.fhircore.engine.util.extension.interpolate

/**
* This class is used to fire rules used to extract and manipulate data from FHIR resources.
Expand Down Expand Up @@ -73,17 +74,18 @@ class ResourceDataRulesExecutor @Inject constructor(val rulesFactory: RulesFacto
listResourceDataStateMap: SnapshotStateMap<String, SnapshotStateList<ResourceData>>,
) {
listProperties.resources.forEach { listResource ->
// Initialize to be updated incrementally as resources are transformed into ResourceData
// A new list is required on each iteration
val resourceDataSnapshotStateList = mutableStateListOf<ResourceData>()
listResourceDataStateMap[listProperties.id] = resourceDataSnapshotStateList

filteredListResources(relatedResourcesMap, listResource)
filteredListResources(relatedResourcesMap, listResource, computedValuesMap)
.mapToResourceData(
listResource = listResource,
listResourceConfig = listResource,
relatedResourcesMap = relatedResourcesMap,
ruleConfigs = listProperties.registerCard.rules,
computedValuesMap = computedValuesMap,
resourceDataSnapshotStateList = resourceDataSnapshotStateList,
listResourceDataStateMap = listResourceDataStateMap,
)
}
}
Expand All @@ -107,36 +109,64 @@ class ResourceDataRulesExecutor @Inject constructor(val rulesFactory: RulesFacto
}

private fun List<Resource>.mapToResourceData(
listResource: ListResource,
listResourceConfig: ListResourceConfig,
relatedResourcesMap: Map<String, List<Resource>>,
ruleConfigs: List<RuleConfig>,
computedValuesMap: Map<String, Any>,
resourceDataSnapshotStateList: SnapshotStateList<ResourceData>,
listResourceDataStateMap: SnapshotStateMap<String, SnapshotStateList<ResourceData>>,
) {
this.forEach { resource ->
this.forEach { baseListResource ->
val relatedResourcesQueue =
ArrayDeque<Pair<Resource, List<ListResourceConfig>>>().apply {
addFirst(Pair(baseListResource, listResourceConfig.relatedResources))
}

val listItemRelatedResources = mutableMapOf<String, List<Resource>>()
listResource.relatedResources.forEach { relatedListResource ->
val retrieveRelatedResources: List<Resource>? =
relatedListResource.fhirPathExpression.let {
while (relatedResourcesQueue.isNotEmpty()) {
val (currentResource, currentListResourceConfig) = relatedResourcesQueue.removeFirst()
currentListResourceConfig.forEach { relatedListResourceConfig ->
val retrievedRelatedResources: List<Resource> =
rulesFactory.rulesEngineService.retrieveRelatedResources(
resource = resource,
resource = currentResource,
relatedResourceKey =
relatedListResource.relatedResourceId ?: relatedListResource.resourceType.name,
referenceFhirPathExpression = it,
relatedListResourceConfig.relatedResourceId
?: relatedListResourceConfig.resourceType.name,
referenceFhirPathExpression = relatedListResourceConfig.fhirPathExpression,
relatedResourcesMap = relatedResourcesMap,
)
}
if (!retrieveRelatedResources.isNullOrEmpty()) {
listItemRelatedResources[
relatedListResource.id ?: relatedListResource.resourceType.name,
] =
if (!relatedListResource.conditionalFhirPathExpression.isNullOrEmpty()) {
rulesFactory.rulesEngineService.filterResources(
retrieveRelatedResources,
relatedListResource.conditionalFhirPathExpression,
)
} else {
retrieveRelatedResources

val interpolatedConditionalFhirPathExpression =
relatedListResourceConfig.conditionalFhirPathExpression?.interpolate(computedValuesMap)

rulesFactory.rulesEngineService
.filterResources(
resources = retrievedRelatedResources,
conditionalFhirPathExpression = interpolatedConditionalFhirPathExpression,
)
.also { filteredResources ->
// Add to queue for processing
filteredResources.forEach {
relatedResourcesQueue.addLast(Pair(it, relatedListResourceConfig.relatedResources))
}

// Apply configurable sorting to related resources
val sortConfig = relatedListResourceConfig.sortConfig
if (sortConfig == null || sortConfig.fhirPathExpression.isBlank()) {
listItemRelatedResources[
relatedListResourceConfig.id ?: relatedListResourceConfig.resourceType.name,
] = filteredResources
} else {
listItemRelatedResources[
relatedListResourceConfig.id ?: relatedListResourceConfig.resourceType.name,
] =
rulesFactory.rulesEngineService.sortResources(
resources = filteredResources,
fhirPathExpression = sortConfig.fhirPathExpression,
dataType = sortConfig.dataType.name,
order = sortConfig.order.name,
) ?: filteredResources
}
}
}
}
Expand All @@ -146,19 +176,19 @@ class ResourceDataRulesExecutor @Inject constructor(val rulesFactory: RulesFacto
ruleConfigs = ruleConfigs,
repositoryResourceData =
RepositoryResourceData(
resourceRulesEngineFactId = null,
resource = resource,
resourceRulesEngineFactId = listResourceConfig.id,
resource = baseListResource,
relatedResourcesMap = listItemRelatedResources,
),
params = emptyMap(),
)

resourceDataSnapshotStateList.add(
ResourceData(
baseResourceId = resource.logicalId.extractLogicalIdUuid(),
baseResourceType = resource.resourceType,
computedValuesMap =
computedValuesMap.plus(listComputedValuesMap), // Reuse computed values
baseResourceId = baseListResource.logicalId,
baseResourceType = baseListResource.resourceType,
computedValuesMap = computedValuesMap.plus(listComputedValuesMap),
listResourceDataMap = listResourceDataStateMap,
),
)
}
Expand All @@ -167,41 +197,36 @@ class ResourceDataRulesExecutor @Inject constructor(val rulesFactory: RulesFacto
/**
* This function returns a list of filtered resources. The required list is obtained from
* [relatedResourceMap], then a filter is applied based on the condition returned from the
* extraction of the [ListResource] conditional FHIR path expression
* extraction of the [ListResourceConfig] conditional FHIR path expression. The list is sorted if
* configurations for sorting are provided.
*/
private fun filteredListResources(
relatedResourceMap: Map<String, List<Resource>>,
listResource: ListResource,
listResource: ListResourceConfig,
computedValuesMap: Map<String, Any>,
): List<Resource> {
val relatedResourceKey = listResource.relatedResourceId ?: listResource.resourceType.name
val newListRelatedResources = relatedResourceMap[relatedResourceKey]
val interpolatedConditionalFhirPathExpression =
listResource.conditionalFhirPathExpression?.interpolate(computedValuesMap)

// conditionalFhirPath expression e.g. "Task.status == 'ready'" to filter tasks that are due
// Filter by condition derived from fhirPathExpression otherwise return original or empty list
val resources =
if (
newListRelatedResources != null &&
!listResource.conditionalFhirPathExpression.isNullOrEmpty()
) {
rulesFactory.rulesEngineService.filterResources(
resources = newListRelatedResources,
conditionalFhirPathExpression = listResource.conditionalFhirPathExpression,
)
} else {
newListRelatedResources ?: listOf()
}
rulesFactory.rulesEngineService.filterResources(
resources = relatedResourceMap[relatedResourceKey],
conditionalFhirPathExpression = interpolatedConditionalFhirPathExpression,
)

// Sort resources if valid sort configuration is provided
val sortConfig = listResource.sortConfig

// Sort resources if sort configuration is provided
return if (sortConfig != null && sortConfig.fhirPathExpression.isNotEmpty()) {
return if (sortConfig == null || sortConfig.fhirPathExpression.isEmpty()) {
resources
} else {
rulesFactory.rulesEngineService.sortResources(
resources = resources,
fhirPathExpression = sortConfig.fhirPathExpression,
dataType = sortConfig.dataType.name,
order = sortConfig.order.name,
) ?: resources
} else {
resources
}
}
}
Loading

0 comments on commit 8f2c558

Please sign in to comment.