Skip to content

Commit f42e804

Browse files
hamza-vdqiariejingtang10dubdabasoduba
authored
Add Launch and Submission Timestamps to QR (#2672)
* Implementation for launch and submission timestamps addition * Update launch time stamp value to latest * PR feedback changes * Refactor QuestionnaireResponse assertion logic * Run spotlessApply --------- Co-authored-by: Simon Kiarie <[email protected]> Co-authored-by: Jing Tang <[email protected]> Co-authored-by: Simon Njoroge <[email protected]> Co-authored-by: Benjamin Mwalimu <[email protected]>
1 parent 92ca237 commit f42e804

File tree

5 files changed

+177
-29
lines changed

5 files changed

+177
-29
lines changed

datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import com.google.android.fhir.datacapture.extensions.isHelpCode
4242
import com.google.android.fhir.datacapture.extensions.isHidden
4343
import com.google.android.fhir.datacapture.extensions.isPaginated
4444
import com.google.android.fhir.datacapture.extensions.isRepeatedGroup
45+
import com.google.android.fhir.datacapture.extensions.launchTimestamp
4546
import com.google.android.fhir.datacapture.extensions.localizedTextSpanned
4647
import com.google.android.fhir.datacapture.extensions.maxValue
4748
import com.google.android.fhir.datacapture.extensions.maxValueCqfCalculatedValueExpression
@@ -64,6 +65,7 @@ import com.google.android.fhir.datacapture.validation.Valid
6465
import com.google.android.fhir.datacapture.validation.ValidationResult
6566
import com.google.android.fhir.datacapture.views.QuestionTextConfiguration
6667
import com.google.android.fhir.datacapture.views.QuestionnaireViewItem
68+
import java.util.Date
6769
import kotlinx.coroutines.flow.MutableStateFlow
6870
import kotlinx.coroutines.flow.SharingStarted
6971
import kotlinx.coroutines.flow.StateFlow
@@ -74,6 +76,7 @@ import kotlinx.coroutines.flow.stateIn
7476
import kotlinx.coroutines.flow.update
7577
import kotlinx.coroutines.flow.withIndex
7678
import kotlinx.coroutines.launch
79+
import org.hl7.fhir.r4.model.DateTimeType
7780
import org.hl7.fhir.r4.model.Questionnaire
7881
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent
7982
import org.hl7.fhir.r4.model.QuestionnaireResponse
@@ -160,6 +163,8 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
160163
.forEach { questionnaireResponse.addItem(it.createQuestionnaireResponseItem()) }
161164
}
162165
}
166+
// Add extension for questionnaire launch time stamp
167+
questionnaireResponse.launchTimestamp = DateTimeType(Date())
163168
questionnaireResponse.packRepeatedGroups(questionnaire)
164169
}
165170

@@ -475,6 +480,8 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
475480
)
476481
.map { it.copy() }
477482
unpackRepeatedGroups(this@QuestionnaireViewModel.questionnaire)
483+
// Use authored as a submission time stamp
484+
authored = Date()
478485
}
479486
}
480487

datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreQuestionnaireResponses.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@
1616

1717
package com.google.android.fhir.datacapture.extensions
1818

19+
import org.hl7.fhir.r4.model.DateTimeType
20+
import org.hl7.fhir.r4.model.Extension
1921
import org.hl7.fhir.r4.model.Questionnaire
2022
import org.hl7.fhir.r4.model.QuestionnaireResponse
2123

24+
internal const val EXTENSION_LAST_LAUNCHED_TIMESTAMP: String =
25+
"http://github.com/google-android/questionnaire-lastLaunched-timestamp"
26+
2227
/** Pre-order list of all questionnaire response items in the questionnaire. */
2328
val QuestionnaireResponse.allItems: List<QuestionnaireResponse.QuestionnaireResponseItemComponent>
2429
get() = item.flatMap { it.descendant }
@@ -146,3 +151,20 @@ private fun unpackRepeatedGroups(
146151
listOf(questionnaireResponseItem)
147152
}
148153
}
154+
155+
/**
156+
* Adds a launch timestamp extension to the Questionnaire Response. If the extension @see
157+
* EXTENSION_LAUNCH_TIMESTAMP already exists, it updates its value; otherwise, it adds a new one.
158+
*/
159+
internal var QuestionnaireResponse.launchTimestamp: DateTimeType?
160+
get() {
161+
val extension = this.extension.firstOrNull { it.url == EXTENSION_LAST_LAUNCHED_TIMESTAMP }
162+
return extension?.value as? DateTimeType
163+
}
164+
set(value) {
165+
extension.find { it.url == EXTENSION_LAST_LAUNCHED_TIMESTAMP }?.setValue(value)
166+
?: run {
167+
// Add a new extension if none exists
168+
extension.add(Extension(EXTENSION_LAST_LAUNCHED_TIMESTAMP, value))
169+
}
170+
}

datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelParameterizedTest.kt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.google.android.fhir.datacapture.QuestionnaireFragment.Companion.EXTRA
2929
import com.google.android.fhir.datacapture.QuestionnaireFragment.Companion.EXTRA_QUESTIONNAIRE_RESPONSE_JSON_STRING
3030
import com.google.android.fhir.datacapture.QuestionnaireFragment.Companion.EXTRA_QUESTIONNAIRE_RESPONSE_JSON_URI
3131
import com.google.android.fhir.datacapture.QuestionnaireFragment.Companion.EXTRA_SHOW_REVIEW_PAGE_FIRST
32+
import com.google.android.fhir.datacapture.extensions.EXTENSION_LAST_LAUNCHED_TIMESTAMP
3233
import com.google.android.fhir.datacapture.testing.DataCaptureTestApplication
3334
import com.google.common.truth.Truth.assertThat
3435
import java.io.File
@@ -89,7 +90,7 @@ class QuestionnaireViewModelParameterizedTest(
8990
val viewModel = createQuestionnaireViewModel(questionnaire)
9091

9192
runTest {
92-
assertResourceEquals(
93+
assertQuestionnaireResponseEqualsIgnoringTimestamps(
9394
viewModel.getQuestionnaireResponse(),
9495
QuestionnaireResponse().apply {
9596
this.questionnaire = "http://www.sample-org/FHIR/Resources/Questionnaire/a-questionnaire"
@@ -135,7 +136,12 @@ class QuestionnaireViewModelParameterizedTest(
135136

136137
val viewModel = createQuestionnaireViewModel(questionnaire, questionnaireResponse)
137138

138-
runTest { assertResourceEquals(viewModel.getQuestionnaireResponse(), questionnaireResponse) }
139+
runTest {
140+
assertQuestionnaireResponseEqualsIgnoringTimestamps(
141+
viewModel.getQuestionnaireResponse(),
142+
questionnaireResponse,
143+
)
144+
}
139145
}
140146

141147
private fun createQuestionnaireViewModel(
@@ -187,6 +193,28 @@ class QuestionnaireViewModelParameterizedTest(
187193
.isEqualTo(printer.encodeResourceToString(expected))
188194
}
189195

196+
/**
197+
* Asserts that the `expected` and the `actual` Questionnaire Responses are equal ignoring the
198+
* stamp values
199+
*/
200+
fun assertQuestionnaireResponseEqualsIgnoringTimestamps(
201+
actual: QuestionnaireResponse,
202+
expected: QuestionnaireResponse,
203+
) {
204+
val actualResponseWithoutTimestamp =
205+
actual.copy().apply {
206+
extension.removeIf { ext -> ext.url == EXTENSION_LAST_LAUNCHED_TIMESTAMP }
207+
authored = null
208+
}
209+
val expectedResponseWithoutTimestamp =
210+
expected.copy().apply {
211+
extension.removeIf { ext -> ext.url == EXTENSION_LAST_LAUNCHED_TIMESTAMP }
212+
authored = null
213+
}
214+
assertThat(printer.encodeResourceToString(actualResponseWithoutTimestamp))
215+
.isEqualTo(printer.encodeResourceToString(expectedResponseWithoutTimestamp))
216+
}
217+
190218
@JvmStatic
191219
@Parameters
192220
fun parameters() =

0 commit comments

Comments
 (0)