Skip to content

Commit

Permalink
Merge branch '2639-async-process-for-get-questionnaire-pages' of gith…
Browse files Browse the repository at this point in the history
…ub.com:opensrp/android-fhir into 2639-async-process-for-get-questionnaire-pages
  • Loading branch information
FikriMilano committed Aug 2, 2024
2 parents 64deedb + 34121d6 commit 4af8cc9
Show file tree
Hide file tree
Showing 31 changed files with 523 additions and 152 deletions.
6 changes: 4 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
// "platform": "33",
// "build_tools": "33.0.1"
// }
}
},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "java -version",
// See https://github.com/google/android-fhir/issues/2614, which is blocked by
// https://github.com/akhildevelops/devcontainer-features/issues/9.
"postCreateCommand": "sudo chown -R vscode:vscode /opt/android/"

// Configure tool-specific properties.
// "customizations": {},
Expand Down
28 changes: 8 additions & 20 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@ import org.gradle.api.artifacts.DependencyConstraint
import org.gradle.kotlin.dsl.exclude

object Dependencies {
object Cql {
const val evaluator = "org.opencds.cqf.fhir:cqf-fhir-cr:${Versions.Cql.clinicalReasoning}"
const val evaluatorFhirJackson =
"org.opencds.cqf.fhir:cqf-fhir-jackson:${Versions.Cql.clinicalReasoning}"
const val evaluatorFhirUtilities =
"org.opencds.cqf.fhir:cqf-fhir-utility:${Versions.Cql.clinicalReasoning}"
}

object HapiFhir {
const val fhirBaseModule = "ca.uhn.hapi.fhir:hapi-fhir-base"
const val fhirClientModule = "ca.uhn.hapi.fhir:hapi-fhir-client"
Expand Down Expand Up @@ -139,11 +131,6 @@ object Dependencies {
const val xmlUnit = "org.xmlunit:xmlunit-core:${Versions.xmlUnit}"

object Versions {

object Cql {
const val clinicalReasoning = "3.0.0-PRE9-SNAPSHOT"
}

const val androidFhirCommon = "0.1.0-alpha05"
const val androidFhirEngine = "0.1.0-beta05"
const val androidFhirKnowledge = "0.1.0-alpha03"
Expand Down Expand Up @@ -195,16 +182,17 @@ object Dependencies {
}

fun Configuration.removeIncompatibleDependencies() {
exclude(module = "xpp3")
exclude(module = "xpp3_min")
exclude(module = "xmlpull")
exclude(module = "hapi-fhir-caching-caffeine")
exclude(module = "javax.json")
exclude(module = "jcl-over-slf4j")
exclude(group = "org.apache.httpcomponents")
exclude(group = "org.antlr", module = "antlr4")
exclude(group = "org.eclipse.persistence", module = "org.eclipse.persistence.moxy")
exclude(module = "hapi-fhir-caching-caffeine")
exclude(module = "xmlpull")
exclude(module = "xpp3")
exclude(module = "xpp3_min")
exclude(group = "ch.qos.logback", module = "logback-classic")
exclude(group = "com.github.ben-manes.caffeine", module = "caffeine")
exclude(group = "org.eclipse.persistence", module = "org.eclipse.persistence.moxy")
exclude(group = "org.antlr", module = "antlr4")
exclude(group = "org.apache.httpcomponents")
}

fun hapiFhirConstraints(): Map<String, DependencyConstraint.() -> Unit> {
Expand Down
19 changes: 16 additions & 3 deletions buildSrc/src/main/kotlin/LicenseeConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,24 @@ fun Project.configureLicensee() {
}

// Jakarta XML Binding API
allowDependency("jakarta.xml.bind", "jakarta.xml.bind-api", "2.3.3") {
allowDependency("jakarta.xml.bind", "jakarta.xml.bind-api", "4.0.1") {
because("BSD 3-clause.")
}

// Jakarta Activation API 2.1 Specification
allowDependency("jakarta.activation", "jakarta.activation-api", "1.2.2") {
allowDependency("jakarta.activation", "jakarta.activation-api", "2.1.2") {
because(
"Licensed under Eclipse Distribution License 1.0. http://www.eclipse.org/org/documents/edl-v10.php",
)
}

// Jakarta Annotation API 2.1 Specification
allowDependency("jakarta.annotation", "jakarta.annotation-api", "2.1.1") {
because(
"Licensed under EPL 2.0",
)
}

// Javax Annotation API
allowDependency("javax.annotation", "javax.annotation-api", "1.3.2") {
because("Dual-licensed under CDDL 1.1 and GPL v2 with classpath exception.")
Expand All @@ -108,7 +115,7 @@ fun Project.configureLicensee() {
because("BSD 3-clause. http://www.antlr.org/license.html")
}
// ANTLR 4
allowDependency("org.antlr", "antlr4-runtime", "4.10.1") {
allowDependency("org.antlr", "antlr4-runtime", "4.13.1") {
because("BSD 3-clause. http://www.antlr.org/license.html")
}

Expand Down Expand Up @@ -195,6 +202,12 @@ fun Project.configureLicensee() {
allowDependency("com.ibm.icu", "icu4j", "72.1") {
because("BSD, part MIT and Apache 2.0. https://github.com/unicode-org/icu/blob/main/LICENSE")
}

// Logback
allowDependency("ch.qos.logback", "logback-classic", "1.4.14") { because("LGPL") }

// Logback
allowDependency("ch.qos.logback", "logback-core", "1.4.14") { because("LGPL") }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import com.google.android.fhir.datacapture.extensions.createQuestionnaireRespons
import com.google.android.fhir.datacapture.extensions.entryMode
import com.google.android.fhir.datacapture.extensions.filterByCodeInNameExtension
import com.google.android.fhir.datacapture.extensions.flattened
import com.google.android.fhir.datacapture.extensions.forEachItemPair
import com.google.android.fhir.datacapture.extensions.hasDifferentAnswerSet
import com.google.android.fhir.datacapture.extensions.isDisplayItem
import com.google.android.fhir.datacapture.extensions.isEnableWhenReferencedBy
Expand Down Expand Up @@ -626,16 +627,13 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
/** Travers all [calculatedExpression] within a [Questionnaire] and evaluate them. */
private suspend fun initializeCalculatedExpressions() {
expressionEvaluator.detectExpressionCyclicDependency(questionnaire.item)
val itemsToBeCalculated =
questionnaire.item.flattened().filter { qItem -> qItem.calculatedExpression != null }
itemsToBeCalculated.forEach { qItem ->
// TODO: Traverse the two trees in parallel and match based on the pairs, the current
// implementation does not work well with nested items and repeated groups
// https://github.com/google/android-fhir/issues/2618
val qrItem =
questionnaireResponse.allItems.find { qrItem -> qrItem.linkId == qItem.linkId }
?: return@forEach
updateAnswerWithCalculatedExpression(qItem, qrItem)
questionnaire.forEachItemPair(questionnaireResponse) {
questionnaireItem,
questionnaireResponseItem,
->
if (questionnaireItem.calculatedExpression != null) {
updateAnswerWithCalculatedExpression(questionnaireItem, questionnaireResponseItem)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.Expression
import org.hl7.fhir.r4.model.Extension
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent
import org.hl7.fhir.r4.model.QuestionnaireResponse
import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType

Expand Down Expand Up @@ -160,6 +163,68 @@ enum class EntryMode(val value: String) {
;

companion object {
fun from(type: String?): EntryMode? = values().find { it.value == type }
fun from(type: String?): EntryMode? = entries.find { it.value == type }
}
}

/**
* Applies `forEach` on each questionnaire item and questionnaire response item pair in the
* questionnaire and the given `questionnaireResponse`.
*
* Questionnaire items and questionnaire response items are visited in pre-order.
*
* Items nested under repeated groups and repeated questions will be repeated for each repeated
* group instance or answer provided by the user.
*
* Note: use this function only with a questionnaire response that has been packed using
* [QuestionnaireResponse.packRepeatedGroups].
*/
internal suspend fun Questionnaire.forEachItemPair(
questionnaireResponse: QuestionnaireResponse,
forEach:
suspend (
questionnaireItem: QuestionnaireItemComponent,
questionnaireResponseItem: QuestionnaireResponseItemComponent,
) -> Unit,
) {
forEachItemPair(item, questionnaireResponse.item, forEach)
}

private suspend fun forEachItemPair(
questionnaireItems: List<QuestionnaireItemComponent>,
questionnaireResponseItems: List<QuestionnaireResponseItemComponent>,
forEach:
suspend (
questionnaireItem: QuestionnaireItemComponent,
questionnaireResponseItem: QuestionnaireResponseItemComponent,
) -> Unit,
) {
require(questionnaireItems.size == questionnaireResponseItems.size)
questionnaireItems.zip(questionnaireResponseItems).forEach {
(questionnaireItem, questionnaireResponseItem) ->
require(questionnaireItem.linkId == questionnaireResponseItem.linkId)

// Apply forEach on the current questionnaire item and questionnaire response item
forEach(questionnaireItem, questionnaireResponseItem)

// For non-repeated groups, simply match the child questionnaire items with child questionnaire
// response items.
if (
questionnaireItem.type == Questionnaire.QuestionnaireItemType.GROUP &&
!questionnaireItem.repeats &&
questionnaireItem.item.isNotEmpty()
) {
forEachItemPair(questionnaireItem.item, questionnaireResponseItem.item, forEach)
}

// The following block handles two separate cases:
// 1. questionnaire items nested under repeated group are repeated for each instance of the
// repeated group, each represented as an answer components in the questionnaire response item.
// 2. questionnaire items nested directly under question are repeated for each answer.
if (questionnaireItem.repeats && questionnaireItem.item.isNotEmpty()) {
questionnaireResponseItem.answer.forEach {
forEachItemPair(questionnaireItem.item, it.item, forEach)
}
}
}
}
Loading

0 comments on commit 4af8cc9

Please sign in to comment.