Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
ndegwamartin committed Nov 15, 2024
2 parents 00a71a9 + 22a080b commit 82445e8
Show file tree
Hide file tree
Showing 57 changed files with 2,523 additions and 1,359 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ jobs:
force-avd-creation: true
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: ./gradlew clean -PlocalPropertiesFile=local.properties :quest:fhircoreJacocoReport --stacktrace -Pandroid.testInstrumentationRunnerArguments.notPackage=org.smartregister.fhircore.quest.performance
script: ./gradlew clean -PlocalPropertiesFile=local.properties :quest:fhircoreJacocoReport --info -Pandroid.testInstrumentationRunnerArguments.notPackage=org.smartregister.fhircore.quest.performance

- name: Run Quest module unit and instrumentation tests and generate aggregated coverage report (Disabled)
if: false
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1. Added a new class (PdfGenerator) for generating PDF documents from HTML content using Android's WebView and PrintManager
2. Introduced a new class (HtmlPopulator) to populate HTML templates with data from a Questionnaire Response
3. Implemented functionality to launch PDF generation using a configuration setup
- Added Save draft MVP functionality

## [1.1.0] - 2024-02-15

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2021-2023, Ona Systems, Inc.
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ constructor(
context = context,
configService = configService,
metadataResource = resource,
filePath =
subFilePath =
"${KnowledgeManagerUtil.KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER}/${resource.resourceType}/${resource.idElement.idPart}.json",
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.smartregister.fhircore.engine.domain.model.RuleConfig
data class RegisterContentConfig(
val separator: String? = null,
val display: String? = null,
val placeholderColor: String? = null,
val rules: List<RuleConfig>? = null,
val visible: Boolean? = null,
val computedRules: List<String>? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ data class CompoundTextProperties(
val textCase: TextCase? = null,
val overflow: TextOverFlow? = null,
val letterSpacing: Int = 0,
val textInnerPadding: Int = 0,
) : ViewProperties(), Parcelable {
override fun interpolate(computedValuesMap: Map<String, Any>): CompoundTextProperties {
return this.copy(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.data.local

import androidx.collection.LruCache
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
import org.smartregister.fhircore.engine.util.DispatcherProvider

@Singleton
class ContentCache @Inject constructor(private val dispatcherProvider: DispatcherProvider) {
private val maxMemory: Int = (Runtime.getRuntime().maxMemory() / 1024).toInt()
private val cacheSize: Int = maxMemory / 8
private val cache = LruCache<String, Resource>(cacheSize)
private val mutex = Mutex()

suspend fun <T : Resource> saveResource(resource: T): T {
val key = "${resource.resourceType.name}/${resource.idPart}"
return withContext(dispatcherProvider.io()) {
mutex.withLock { cache.put(key, resource.copy()) }
@Suppress("UNCHECKED_CAST")
getResource(resource.resourceType, resource.idPart)!! as T
}
}

fun getResource(type: ResourceType, id: String) = cache["$type/$id"]?.copy()

suspend fun invalidate() =
withContext(dispatcherProvider.io()) { mutex.withLock { cache.evictAll() } }
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ 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
import com.google.android.fhir.getResourceType
import com.google.android.fhir.search.Order
import com.google.android.fhir.search.Search
import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion
Expand Down Expand Up @@ -115,17 +116,33 @@ constructor(
@ApplicationContext open val context: Context,
) {

@Inject lateinit var contentCache: ContentCache

init {
DaggerDefaultRepositoryComponent.create().inject(this)
}

suspend inline fun <reified T : Resource> loadResource(resourceId: String): T? =
fhirEngine.loadResource(resourceId)

@Throws(ResourceNotFoundException::class)
suspend fun loadResource(resourceId: String, resourceType: ResourceType): Resource =
fhirEngine.get(resourceType, resourceId)

@Throws(ResourceNotFoundException::class)
suspend fun loadResource(reference: Reference) =
IdType(reference.reference).let {
fhirEngine.get(ResourceType.fromCode(it.resourceType), it.idPart)
}

suspend inline fun <reified T : Resource> loadResourceFromCache(resourceId: String): T? {
val resourceType = getResourceType(T::class.java)
val resource =
contentCache.getResource(resourceType, resourceId)
?: fhirEngine.loadResource<T>(resourceId)?.let { contentCache.saveResource(it) }
return resource as? T
}

suspend inline fun <reified T : Resource> searchResourceFor(
token: TokenClientParam,
subjectType: ResourceType,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.data.local

import dagger.Component
import javax.inject.Singleton
import org.smartregister.fhircore.engine.di.DispatcherModule

@Singleton
@Component(modules = [DispatcherModule::class])
interface DefaultRepositoryComponent {
fun inject(defaultRepository: DefaultRepository)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import ca.uhn.fhir.context.FhirContext
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport
import ca.uhn.fhir.context.support.IValidationSupport
import ca.uhn.fhir.validation.FhirValidator
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -30,6 +31,8 @@ import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerVali
import org.hl7.fhir.common.hapi.validation.support.UnknownCodeSystemWarningValidationSupport
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator
import org.smartregister.fhircore.engine.util.DispatcherProvider
import org.smartregister.fhircore.engine.util.validation.ResourceValidationRequestHandler

@Module
@InstallIn(SingletonComponent::class)
Expand All @@ -52,4 +55,13 @@ class FhirValidatorModule {
instanceValidator.invalidateCaches()
return fhirContext.newValidator().apply { registerValidatorModule(instanceValidator) }
}

@Provides
@Singleton
fun provideResourceValidationRequestHandler(
fhirValidatorProvider: Lazy<FhirValidator>,
dispatcherProvider: DispatcherProvider,
): ResourceValidationRequestHandler {
return ResourceValidationRequestHandler(fhirValidatorProvider.get(), dispatcherProvider)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,9 @@ constructor(
}
source.setParameter(Task.SP_PERIOD, period)
source.setParameter(ActivityDefinition.SP_VERSION, IntegerType(index))
val structureMapId = IdType(action.transform).idPart
val structureMap = defaultRepository.loadResourceFromCache<StructureMap>(structureMapId)

val structureMap = fhirEngine.get<StructureMap>(IdType(action.transform).idPart)
structureMapUtilities.transform(
transformSupportServices.simpleWorkerContext,
source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ object AlertDialogue {
@StringRes confirmButtonText: Int = R.string.questionnaire_alert_confirm_button_title,
neutralButtonListener: ((d: DialogInterface) -> Unit)? = null,
@StringRes neutralButtonText: Int = R.string.questionnaire_alert_neutral_button_title,
negativeButtonListener: ((d: DialogInterface) -> Unit)? = null,
@StringRes negativeButtonText: Int = R.string.questionnaire_alert_negative_button_title,
cancellable: Boolean = false,
options: Array<AlertDialogListItem>? = null,
): AlertDialog {
Expand All @@ -71,6 +73,9 @@ object AlertDialogue {
confirmButtonListener?.let {
setPositiveButton(confirmButtonText) { d, _ -> confirmButtonListener.invoke(d) }
}
negativeButtonListener?.let {
setNegativeButton(negativeButtonText) { d, _ -> negativeButtonListener.invoke(d) }
}
options?.run { setSingleChoiceItems(options.map { it.value }.toTypedArray(), -1, null) }
}
.show()
Expand Down Expand Up @@ -172,6 +177,8 @@ object AlertDialogue {
@StringRes confirmButtonText: Int,
neutralButtonListener: ((d: DialogInterface) -> Unit),
@StringRes neutralButtonText: Int,
negativeButtonListener: ((d: DialogInterface) -> Unit),
@StringRes negativeButtonText: Int,
cancellable: Boolean = true,
options: List<AlertDialogListItem>? = null,
): AlertDialog {
Expand All @@ -184,6 +191,8 @@ object AlertDialogue {
confirmButtonText = confirmButtonText,
neutralButtonListener = neutralButtonListener,
neutralButtonText = neutralButtonText,
negativeButtonListener = negativeButtonListener,
negativeButtonText = negativeButtonText,
cancellable = cancellable,
options = options?.toTypedArray(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,26 @@ object KnowledgeManagerUtil {
const val KNOWLEDGE_MANAGER_ASSETS_SUBFOLDER = "km"
private val fhirContext = FhirContext.forR4Cached()

/**
* Util method that creates a physical file and writes the Metadata FHIR resource content to it.
* Note the filepath provided is appended to the apps private directory as returned by
* Context.filesDir
*
* @param subFilePath the path of the file but within the apps private directory
* {Context.filesDir}
* @param metadataResource the actual FHIR Resource of type MetadataResource
* @param configService the configuration service
* @param context the application context
* @return File the file object after creating and writing
*/
fun writeToFile(
filePath: String,
subFilePath: String,
metadataResource: MetadataResource,
configService: ConfigService,
context: Context,
): File =
context
.createFileInPrivateDirectory(filePath)
.createFileInPrivateDirectory(subFilePath)
.also { it.parentFile?.mkdirs() }
.apply {
writeText(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import org.hl7.fhir.r4.model.Expression
import org.hl7.fhir.r4.model.IdType
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.QuestionnaireResponse
import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus
import org.hl7.fhir.r4.model.StringType
import org.smartregister.fhircore.engine.configuration.LinkIdType
import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig
Expand Down Expand Up @@ -292,3 +293,19 @@ suspend fun Questionnaire.prepopulateUniqueIdAssignment(
}
}
}

/**
* Determines the [QuestionnaireResponse.Status] depending on the [saveDraft] and [isEditable]
* values contained in the [QuestionnaireConfig]
*
* returns [COMPLETED] when [isEditable] is [true] returns [INPROGRESS] when [saveDraft] is [true]
*/
fun QuestionnaireConfig.questionnaireResponseStatus(): String? {
return if (this.isEditable()) {
QuestionnaireResponseStatus.COMPLETED.toCode()
} else if (this.saveDraft) {
QuestionnaireResponseStatus.INPROGRESS.toCode()
} else {
null
}
}
Loading

0 comments on commit 82445e8

Please sign in to comment.