From 9c094f74678034fe67356b2442ac047f3691f06a Mon Sep 17 00:00:00 2001 From: fikrimilano Date: Fri, 13 Sep 2024 08:23:15 +0700 Subject: [PATCH 1/7] Refactor to use PdfConfig Initially using QuestionnaireConfig for simplicity. --- .../engine/configuration/PdfConfig.kt | 43 +++++++++++ .../configuration/QuestionnaireConfig.kt | 4 -- .../engine/domain/model/ActionConfig.kt | 2 + .../quest/ui/pdf/PdfLauncherFragment.kt | 16 ++--- .../quest/util/extensions/ConfigExtensions.kt | 11 ++- .../quest/ui/pdf/PdfLauncherFragmentTest.kt | 72 ++++++++++--------- 6 files changed, 95 insertions(+), 53 deletions(-) create mode 100644 android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt new file mode 100644 index 0000000000..9f9b5f6e45 --- /dev/null +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt @@ -0,0 +1,43 @@ +/* + * 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.configuration + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable +import org.hl7.fhir.r4.model.ResourceType +import org.smartregister.fhircore.engine.util.extension.interpolate + +@Serializable +@Parcelize +data class PdfConfig( + val pdfTitle: String? = null, + val pdfTitleSuffix: String? = null, + val pdfStructureReference: String? = null, + val subjectReference: String? = null, + val subjectType: ResourceType? = null, + val questionnaireReferences: List = emptyList() +) : java.io.Serializable, Parcelable { + + fun interpolate(computedValuesMap: Map) = + this.copy( + pdfTitle = pdfTitle?.interpolate(computedValuesMap), + pdfStructureReference = pdfStructureReference?.interpolate(computedValuesMap), + subjectReference = subjectReference?.interpolate(computedValuesMap), + questionnaireReferences = questionnaireReferences.map { it.interpolate(computedValuesMap) }, + ) +} diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/QuestionnaireConfig.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/QuestionnaireConfig.kt index 60da3c1f83..1307c501e9 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/QuestionnaireConfig.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/QuestionnaireConfig.kt @@ -66,8 +66,6 @@ data class QuestionnaireConfig( val managingEntityRelationshipCode: String? = null, val uniqueIdAssignment: UniqueIdAssignmentConfig? = null, val linkIds: List? = null, - val htmlBinaryId: String? = null, - val htmlTitle: String? = null, ) : java.io.Serializable, Parcelable { fun interpolate(computedValuesMap: Map) = @@ -102,8 +100,6 @@ data class QuestionnaireConfig( uniqueIdAssignment?.copy(linkId = uniqueIdAssignment.linkId.interpolate(computedValuesMap)), linkIds = linkIds?.onEach { it.linkId.interpolate(computedValuesMap) }, saveButtonText = saveButtonText?.interpolate(computedValuesMap), - htmlBinaryId = htmlBinaryId?.interpolate(computedValuesMap), - htmlTitle = htmlTitle?.interpolate(computedValuesMap), ) } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/ActionConfig.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/ActionConfig.kt index 0bd988d40b..3b9f5b3acd 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/ActionConfig.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/ActionConfig.kt @@ -22,6 +22,7 @@ import kotlinx.parcelize.Parcelize import kotlinx.serialization.Serializable import org.hl7.fhir.r4.model.Enumerations import org.hl7.fhir.r4.model.ResourceType +import org.smartregister.fhircore.engine.configuration.PdfConfig import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig import org.smartregister.fhircore.engine.configuration.profile.ManagingEntityConfig import org.smartregister.fhircore.engine.configuration.workflow.ActionTrigger @@ -42,6 +43,7 @@ data class ActionConfig( val toolBarHomeNavigation: ToolBarHomeNavigation = ToolBarHomeNavigation.OPEN_DRAWER, val popNavigationBackStack: Boolean? = null, val multiSelectViewConfig: MultiSelectViewConfig? = null, + val pdfConfig: PdfConfig? = null, ) : Parcelable, java.io.Serializable { fun paramsBundle(computedValuesMap: Map = emptyMap()): Bundle = Bundle().apply { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt index e20d72e111..c0f5bc31c5 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt @@ -30,7 +30,7 @@ import org.hl7.fhir.r4.model.Binary import org.hl7.fhir.r4.model.QuestionnaireResponse import org.jetbrains.annotations.VisibleForTesting import org.smartregister.fhircore.engine.R -import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig +import org.smartregister.fhircore.engine.configuration.PdfConfig import org.smartregister.fhircore.engine.pdf.HtmlPopulator import org.smartregister.fhircore.engine.util.extension.decodeJson import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid @@ -38,7 +38,7 @@ import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid /** * A fragment for generating and displaying a PDF based on a questionnaire response. * - * This fragment uses the provided [QuestionnaireConfig] to retrieve a questionnaire response, + * This fragment uses the provided [PdfConfig] to retrieve a questionnaire response, * populate an HTML template with the response data, and generate a PDF. */ @AndroidEntryPoint @@ -52,7 +52,7 @@ class PdfLauncherFragment : DialogFragment() { super.onCreate(savedInstanceState) if (!this::pdfGenerator.isInitialized) pdfGenerator = PdfGenerator(requireContext()) - val questionnaireConfig = getQuestionnaireConfig() + val pdfConfig = getPdfConfig() val questionnaireId = questionnaireConfig.id.extractLogicalIdUuid() val subjectId = questionnaireConfig.resourceIdentifier!!.extractLogicalIdUuid() @@ -75,12 +75,12 @@ class PdfLauncherFragment : DialogFragment() { /** * Retrieves and decodes the questionnaire configuration from the fragment arguments. * - * @return the decoded [QuestionnaireConfig] object. + * @return the decoded [PdfConfig] object. * @throws IllegalArgumentException if the questionnaire config is not found in arguments. */ - private fun getQuestionnaireConfig(): QuestionnaireConfig { + private fun getPdfConfig(): PdfConfig { val jsonConfig = - requireArguments().getString(EXTRA_QUESTIONNAIRE_CONFIG_KEY) + requireArguments().getString(EXTRA_PDF_CONFIG_KEY) ?: throw IllegalArgumentException("Questionnaire config not found in arguments") return jsonConfig.decodeJson() } @@ -123,10 +123,10 @@ class PdfLauncherFragment : DialogFragment() { */ fun launch(appCompatActivity: AppCompatActivity, questionnaireConfigJson: String) { PdfLauncherFragment() - .apply { arguments = bundleOf(EXTRA_QUESTIONNAIRE_CONFIG_KEY to questionnaireConfigJson) } + .apply { arguments = bundleOf(EXTRA_PDF_CONFIG_KEY to questionnaireConfigJson) } .show(appCompatActivity.supportFragmentManager, PdfLauncherFragment::class.java.simpleName) } - @VisibleForTesting const val EXTRA_QUESTIONNAIRE_CONFIG_KEY = "questionnaire_config" + @VisibleForTesting const val EXTRA_PDF_CONFIG_KEY = "pdf_config" } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt index 713500d2ac..f503c09d1a 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt @@ -225,13 +225,10 @@ fun ActionConfig.handleClickEvent( navController.navigate(MainNavigationScreen.LocationSelector.route, args) } ApplicationWorkflow.LAUNCH_PDF_GENERATION -> { - val questionnaireConfig = actionConfig.questionnaire ?: return - val questionnaireConfigInterpolated = questionnaireConfig.interpolate(computedValuesMap) - val appCompatActivity = (navController.context as AppCompatActivity) - PdfLauncherFragment.launch( - appCompatActivity, - questionnaireConfigInterpolated.encodeJson(), - ) + val pdfConfig = actionConfig.pdfConfig ?: return + val interpolatedPdfConfig = pdfConfig.interpolate(computedValuesMap) + val appCompatActivity = (navController.context as AppCompatActivity) + PdfLauncherFragment.launch(appCompatActivity, interpolatedPdfConfig.encodeJson()) } else -> return } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt index d0b23bb9c6..f9c7151057 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt @@ -32,7 +32,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.robolectric.Robolectric -import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig +import org.smartregister.fhircore.engine.configuration.PdfConfig import org.smartregister.fhircore.engine.util.extension.encodeJson import org.smartregister.fhircore.quest.app.fakes.HiltTestActivity import org.smartregister.fhircore.quest.robolectric.RobolectricTest @@ -53,26 +53,27 @@ class PdfLauncherFragmentTest : RobolectricTest() { @Test fun testPdfGeneration() = runBlocking { - val questionnaireResponse = QuestionnaireResponse() + val questionnaireResponse = QuestionnaireResponse().apply { questionnaire = "Questionnaire/id" } val htmlBinary = Binary().apply { content = "mock content".toByteArray() } coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } returns questionnaireResponse coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary - val questionnaireConfig = - QuestionnaireConfig( - id = "1", - resourceIdentifier = "123", - resourceType = ResourceType.Patient, - htmlBinaryId = "1234", - htmlTitle = "Title", + val pdfConfig = + PdfConfig( + pdfTitle = "title", + pdfTitleSuffix = "suffix", + pdfStructureReference = "Binary/id", + subjectReference = "Patient/id", + subjectType = ResourceType.Patient, + questionnaireReferences = listOf("QuestionnaireResponse/id") ) .encodeJson() val fragmentArgs = Bundle().apply { - putString(PdfLauncherFragment.EXTRA_QUESTIONNAIRE_CONFIG_KEY, questionnaireConfig) + putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) } val activity = Robolectric.buildActivity(HiltTestActivity::class.java).create().resume().get() @@ -99,19 +100,20 @@ class PdfLauncherFragmentTest : RobolectricTest() { questionnaireResponse coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary - val questionnaireConfig = - QuestionnaireConfig( - id = "1", - resourceIdentifier = "123", - resourceType = ResourceType.Patient, - htmlBinaryId = "1234", - htmlTitle = "Title", + val pdfConfig = + PdfConfig( + pdfTitle = "title", + pdfTitleSuffix = "suffix", + pdfStructureReference = "Binary/id", + subjectReference = "Patient/id", + subjectType = ResourceType.Patient, + questionnaireReferences = listOf("QuestionnaireResponse/id") ) .encodeJson() val fragmentArgs = Bundle().apply { - putString(PdfLauncherFragment.EXTRA_QUESTIONNAIRE_CONFIG_KEY, questionnaireConfig) + putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) } val activity = Robolectric.buildActivity(HiltTestActivity::class.java).create().resume().get() @@ -138,19 +140,20 @@ class PdfLauncherFragmentTest : RobolectricTest() { questionnaireResponse coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary - val questionnaireConfig = - QuestionnaireConfig( - id = "1", - resourceIdentifier = "123", - resourceType = ResourceType.Patient, - htmlBinaryId = "1234", - htmlTitle = "Title", + val pdfConfig = + PdfConfig( + pdfTitle = "title", + pdfTitleSuffix = "suffix", + pdfStructureReference = "Binary/id", + subjectReference = "Patient/id", + subjectType = ResourceType.Patient, + questionnaireReferences = listOf("QuestionnaireResponse/id") ) .encodeJson() val fragmentArgs = Bundle().apply { - putString(PdfLauncherFragment.EXTRA_QUESTIONNAIRE_CONFIG_KEY, questionnaireConfig) + putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) } val activity = Robolectric.buildActivity(HiltTestActivity::class.java).create().resume().get() @@ -177,19 +180,20 @@ class PdfLauncherFragmentTest : RobolectricTest() { questionnaireResponse coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary - val questionnaireConfig = - QuestionnaireConfig( - id = "1", - resourceIdentifier = "123", - resourceType = ResourceType.Patient, - htmlBinaryId = "1234", - htmlTitle = "Title", + val pdfConfig = + PdfConfig( + pdfTitle = "title", + pdfTitleSuffix = "suffix", + pdfStructureReference = "Binary/id", + subjectReference = "Patient/id", + subjectType = ResourceType.Patient, + questionnaireReferences = listOf("QuestionnaireResponse/id") ) .encodeJson() val fragmentArgs = Bundle().apply { - putString(PdfLauncherFragment.EXTRA_QUESTIONNAIRE_CONFIG_KEY, questionnaireConfig) + putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) } val activity = Robolectric.buildActivity(HiltTestActivity::class.java).create().resume().get() From eaa586a65c83d92508bfc57f1a180840a1a04797 Mon Sep 17 00:00:00 2001 From: fikrimilano Date: Fri, 13 Sep 2024 08:24:05 +0700 Subject: [PATCH 2/7] Process multi QRs in HtmlPopulator --- .../fhircore/engine/pdf/HtmlPopulator.kt | 60 ++-- .../fhircore/engine/pdf/HtmlPopulatorTest.kt | 333 ++++++++++-------- .../quest/ui/pdf/PdfLauncherFragment.kt | 40 ++- 3 files changed, 257 insertions(+), 176 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt index 28aaf581ed..7a3232e544 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt @@ -21,27 +21,45 @@ import java.util.regex.Pattern import org.hl7.fhir.r4.model.BaseDateTimeType import org.hl7.fhir.r4.model.QuestionnaireResponse import org.smartregister.fhircore.engine.util.extension.allItems +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent +import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.extension.formatDate import org.smartregister.fhircore.engine.util.extension.makeItReadable import org.smartregister.fhircore.engine.util.extension.valueToString +import java.util.Date /** * HtmlPopulator class is responsible for processing an HTML template by replacing custom tags with - * data from a QuestionnaireResponse. The class uses various regex patterns to find and replace - * custom tags such as @is-not-empty, @answer-as-list, @answer, @submitted-date, and @contains. + * data from QuestionnaireResponses. The class uses various regex patterns to find and replace + * custom tags such as @is-not-empty, @answer-as-list, @answer, @submitted-date, @contains, and + * @is-questionnaire-submitted. * - * @property questionnaireResponse The QuestionnaireResponse object containing data for replacement. + * @property questionnaireResponses The QuestionnaireResponses object containing data for replacement. */ class HtmlPopulator( - private val questionnaireResponse: QuestionnaireResponse, + questionnaireResponses: List, ) { + // Store answers as key-value pairs with link-id as key + private val answerMap: MutableMap> = mutableMapOf() - // Map to store questionnaire response items keyed by their linkId - private val questionnaireResponseItemMap = - questionnaireResponse.allItems.associateBy( - keySelector = { it.linkId }, - valueTransform = { it.answer }, - ) + // Store submitted-date of a questionnaire response with link-id as key + private val submittedDateMap: MutableMap = mutableMapOf() + + // Store questionnaire-id related to each questionnaire-response + private val questionnaireIds: MutableList = mutableListOf() + + init { + questionnaireResponses.forEach { questionnaireResponse -> + val questionnaireId = questionnaireResponse.questionnaire.extractLogicalIdUuid() + val answerMap = questionnaireResponse.allItems.associateBy( + keySelector = { "$questionnaireId/${it.linkId}" }, + valueTransform = { it.answer }, + ) + this.answerMap.putAll(answerMap) + this.submittedDateMap[questionnaireId] = questionnaireResponse.meta.lastUpdated ?: Date() + this.questionnaireIds.add(questionnaireId) + } + } /** * Populates the provided HTML template with data from the QuestionnaireResponse. @@ -94,7 +112,7 @@ class HtmlPopulator( private fun processIsNotEmpty(i: Int, html: StringBuilder, matcher: Matcher) { val linkId = matcher.group(1) val content = matcher.group(2) ?: "" - val doesAnswerExist = questionnaireResponseItemMap.getOrDefault(linkId, listOf()).isNotEmpty() + val doesAnswerExist = answerMap.getOrDefault(linkId, listOf()).isNotEmpty() if (doesAnswerExist) { html.replace(i, matcher.end() + i, content) // Start index is the index of '@' symbol, End index is the index after the ')' symbol. @@ -119,7 +137,7 @@ class HtmlPopulator( private fun processAnswerAsList(i: Int, html: StringBuilder, matcher: Matcher) { val linkId = matcher.group(1) val answerAsList = - questionnaireResponseItemMap.getOrDefault(linkId, listOf()).joinToString(separator = "") { + answerMap.getOrDefault(linkId, listOf()).joinToString(separator = "") { answer -> "
  • ${answer.value.valueToString()}
  • " } @@ -137,10 +155,9 @@ class HtmlPopulator( val linkId = matcher.group(1) val dateFormat = matcher.group(2) val answer = - questionnaireResponseItemMap.getOrDefault(linkId, listOf()).joinToString { answer -> - if (dateFormat == null) { - answer.value.valueToString() - } else answer.value.valueToString(dateFormat) + answerMap.getOrDefault(linkId, listOf()).joinToString { answer -> + if (dateFormat == null) answer.value.valueToString() + else answer.value.valueToString(dateFormat) } html.replace(i, matcher.end() + i, answer) } @@ -153,12 +170,13 @@ class HtmlPopulator( * @param matcher The Matcher object for the regex pattern. */ private fun processSubmittedDate(i: Int, html: StringBuilder, matcher: Matcher) { - val dateFormat = matcher.group(1) + val questionnaireId = matcher.group(1) + val dateFormat = matcher.group(2) val date = if (dateFormat == null) { - questionnaireResponse.meta.lastUpdated.formatDate() + submittedDateMap.getOrDefault(questionnaireId, Date()).formatDate() } else { - questionnaireResponse.meta.lastUpdated.formatDate(dateFormat) + submittedDateMap.getOrDefault(questionnaireId, Date()).formatDate(dateFormat) } html.replace(i, matcher.end() + i, date) } @@ -176,7 +194,7 @@ class HtmlPopulator( val indicator = matcher.group(2) ?: "" val content = matcher.group(3) ?: "" val doesAnswerExist = - questionnaireResponseItemMap.getOrDefault(linkId, listOf()).any { + answerMap.getOrDefault(linkId, listOf()).any { when { it.hasValueCoding() -> it.valueCoding.code == indicator it.hasValueStringType() -> it.valueStringType.value.contains(indicator) @@ -203,7 +221,7 @@ class HtmlPopulator( Pattern.compile("@is-not-empty\\('([^']+)'\\)((?s).*?)@is-not-empty\\('\\1'\\)") private val answerAsListPattern = Pattern.compile("@answer-as-list\\('([^']+)'\\)") private val answerPattern = Pattern.compile("@answer\\('([^']+)'(?:,'([^']+)')?\\)") - private val submittedDatePattern = Pattern.compile("@submitted-date(?:\\('([^']+)'\\))?") + private val submittedDatePattern = Pattern.compile("@submitted-date\\('([^']+)'(?:,'([^']+)')?\\)") private val containsPattern = Pattern.compile("@contains\\('([^']+)','([^']+)'\\)((?s).*?)@contains\\('\\1'\\)") } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/pdf/HtmlPopulatorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/pdf/HtmlPopulatorTest.kt index 2a348b4376..74b6ba801c 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/pdf/HtmlPopulatorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/pdf/HtmlPopulatorTest.kt @@ -35,82 +35,93 @@ class HtmlPopulatorTest { @Test fun testIsNotEmptyShouldShowContentWhenAnswerExistInQR() { - val html = "@is-not-empty('link-a')

    Text

    @is-not-empty('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 1", "code 1", "display 1") - }, - ) + val html = "@is-not-empty('1234/link-a')

    Text

    @is-not-empty('1234/link-a')" + val questionnaireResponses = + listOf( + QuestionnaireResponse().apply { + id = "1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 1", "code 1", "display 1") + }, + ) + } } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + ) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

    Text

    ", populatedHtml) } @Test fun testIsNotEmptyShouldHideContentWhenAnswerIsEmptyInQR() { - val html = "@is-not-empty('link-a')

    Text

    @is-not-empty('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { - addItem().apply { - linkId = "link-a" - answer = emptyList() + val html = "@is-not-empty('1234/link-a')

    Text

    @is-not-empty('1234/link-a')" + val questionnaireResponses = + listOf( + QuestionnaireResponse().apply { + id = "1234" + addItem().apply { + linkId = "link-a" + answer = emptyList() + } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + ) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) } @Test fun testIsNotEmptyShouldHideContentWhenAnswerNotExistInQR() { - val html = "@is-not-empty('link-a')

    Text

    @is-not-empty('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { addItem().apply { linkId = "link-a" } } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "@is-not-empty('1234/link-a')

    Text

    @is-not-empty('1234/link-a')" + val questionnaireResponses = + listOf( + QuestionnaireResponse().apply { + id = "1234" + addItem().apply { linkId = "link-a" } } + ) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) } @Test fun testIsNotEmptyShouldHideContentWhenLinkIdNotExistInQR() { - val html = "@is-not-empty('link-a')

    Text

    @is-not-empty('link-a')" - val questionnaireResponse = QuestionnaireResponse() - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "@is-not-empty('1234/link-a')

    Text

    @is-not-empty('1234/link-a')" + val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) } @Test fun testIsNotEmptyShouldShowMalformedTagAndContentIfLinkIdOfBothTagDoesNotMatch() { - val html = "@is-not-empty('link-a')

    Text

    @is-not-empty('link-b')" - val questionnaireResponse = QuestionnaireResponse() - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "@is-not-empty('1234/link-a')

    Text

    @is-not-empty('1234/link-b')" + val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) - Assert.assertEquals("@is-not-empty('link-a')

    Text

    @is-not-empty('link-b')", populatedHtml) + Assert.assertEquals("@is-not-empty('1234/link-a')

    Text

    @is-not-empty('1234/link-b')", populatedHtml) } @Test fun testIsNotEmptyShouldShowMalformedTagAndContentIfOnly1TagExist() { - val html = "@is-not-empty('link-a')

    Text

    " - val questionnaireResponse = QuestionnaireResponse() - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "@is-not-empty('1234/link-a')

    Text

    " + val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) - Assert.assertEquals("@is-not-empty('link-a')

    Text

    ", populatedHtml) + Assert.assertEquals("@is-not-empty('1234/link-a')

    Text

    ", populatedHtml) } @Test fun testIsNotEmptyShouldShowContentAndNestedMalformedTagIfAnswerOfRootTagExist() { - val html = "@is-not-empty('link-a')@is-not-empty('link-b')

    Text

    @is-not-empty('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

    Text

    @is-not-empty('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -121,42 +132,46 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) - Assert.assertEquals("@is-not-empty('link-b')

    Text

    ", populatedHtml) + Assert.assertEquals("@is-not-empty('1234/link-b')

    Text

    ", populatedHtml) } @Test fun testIsNotEmptyShouldHideContentAndNestedMalformedTagIfAnswerOfRootTagIsNotExist() { - val html = "@is-not-empty('link-a')@is-not-empty('link-b')

    Text

    @is-not-empty('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { addItem().apply { linkId = "link-a" } } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

    Text

    @is-not-empty('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" + addItem().apply { linkId = "link-a" } }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) } @Test fun testIsNotEmptyShouldHideContentAndNestedMalformedTagIfAnswerOfRootTagIsEmpty() { - val html = "@is-not-empty('link-a')@is-not-empty('link-b')

    Text

    @is-not-empty('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

    Text

    @is-not-empty('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = emptyList() } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) } @Test fun testIsNotEmptyShouldShowEmptyContentIfAnswerExist() { - val html = "@is-not-empty('link-a')@is-not-empty('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@is-not-empty('1234/link-a')@is-not-empty('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -167,17 +182,18 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) } @Test fun testProcessAnswerAsListShouldShowAnswerAsListWhenAnswerExistInQR() { - val html = "
      @answer-as-list('link-a')
    " - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "
      @answer-as-list('1234/link-a')
    " + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -193,36 +209,39 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("
    • display 1
    • display 2
    ", populatedHtml) } @Test fun testProcessAnswerAsListShouldShowEmptyAnswerAsListWhenAnswerNotExistInQR() { - val html = "
      @answer-as-list('link-a')
    " - val questionnaireResponse = - QuestionnaireResponse().apply { addItem().apply { linkId = "link-a" } } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "
      @answer-as-list('1234/link-a')
    " + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" + addItem().apply { linkId = "link-a" } }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("
      ", populatedHtml) } @Test fun testProcessAnswerAsListShouldShowEmptyAnswerAsListWhenLinkIdNotExistInQR() { - val html = "
        @answer-as-list('link-a')
      " - val questionnaireResponse = QuestionnaireResponse() - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "
        @answer-as-list('1234/link-a')
      " + val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("
        ", populatedHtml) } @Test fun testProcessAnswerShouldShowAnswerWhenAnswerExistInQR() { - val html = "

        @answer('link-a')

        " - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "

        @answer('1234/link-a')

        " + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -231,27 +250,29 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        string 1

        ", populatedHtml) } @Test fun testProcessAnswerShouldShowEmptyAnswerWhenAnswerNotExistInQR() { - val html = "

        @answer('link-a')

        " - val questionnaireResponse = - QuestionnaireResponse().apply { addItem().apply { linkId = "link-a" } } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "

        @answer('1234/link-a')

        " + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" + addItem().apply { linkId = "link-a" } }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        ", populatedHtml) } @Test fun testProcessAnswerShouldShowEmptyAnswerWhenLinkIdNotExistInQR() { - val html = "

        @answer('link-a')

        " - val questionnaireResponse = QuestionnaireResponse() - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "

        @answer('1234/link-a')

        " + val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        ", populatedHtml) } @@ -260,9 +281,10 @@ class HtmlPopulatorTest { fun testProcessAnswerShouldShowDateAnswerWhenAnswerOfTypeDateExistInQR() { val calendar = Calendar.getInstance().apply { set(2024, Calendar.MAY, 14) } val specificDate: Date = calendar.time - val html = "

        @answer('link-a')

        " - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "

        @answer('1234/link-a')

        " + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -273,8 +295,8 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        14-May-2024

        ", populatedHtml) } @@ -283,9 +305,10 @@ class HtmlPopulatorTest { fun testProcessAnswerShouldShowDateAnswerWithFormatWhenDateFormatExistInTheTag() { val calendar = Calendar.getInstance().apply { set(2024, Calendar.MAY, 14) } val specificDate: Date = calendar.time - val html = "

        @answer('link-a','MMMM d, yyyy')

        " - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "

        @answer('1234/link-a','MMMM d, yyyy')

        " + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -296,8 +319,8 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        May 14, 2024

        ", populatedHtml) } @@ -306,10 +329,12 @@ class HtmlPopulatorTest { fun testProcessSubmittedDateShouldShow() { val calendar = Calendar.getInstance().apply { set(2024, Calendar.MAY, 14) } val specificDate: Date = calendar.time - val html = "

        @submitted-date

        " - val questionnaireResponse = - QuestionnaireResponse().apply { meta = Meta().apply { lastUpdated = specificDate } } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "

        @submitted-date('1234')

        " + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" + meta = Meta().apply { lastUpdated = specificDate } }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        14-May-2024

        ", populatedHtml) } @@ -318,19 +343,22 @@ class HtmlPopulatorTest { fun testProcessSubmittedDateShouldShowWithFormatWhenDateFormatExistInTheTag() { val calendar = Calendar.getInstance().apply { set(2024, Calendar.MAY, 14) } val specificDate: Date = calendar.time - val html = "

        @submitted-date('MMMM d, yyyy')

        " - val questionnaireResponse = - QuestionnaireResponse().apply { meta = Meta().apply { lastUpdated = specificDate } } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + val html = "

        @submitted-date('1234','MMMM d, yyyy')

        " + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" + meta = Meta().apply { lastUpdated = specificDate } }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        May 14, 2024

        ", populatedHtml) } @Test fun testProcessContainsShouldShowContentWhenIndicatorCodeMatchesWithAnswerOfTypeCoding() { - val html = "@contains('link-a','code 2')

        Text

        @contains('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@contains('1234/link-a','code 2')

        Text

        @contains('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -346,17 +374,18 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        Text

        ", populatedHtml) } @Test fun testProcessContainsShouldHideContentWhenIndicatorCodeDoesNotMatchWithAnswerOfTypeCoding() { - val html = "@contains('link-a','code 3')

        Text

        @contains('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@contains('1234/link-a','code 3')

        Text

        @contains('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -372,17 +401,18 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) } @Test fun testProcessContainsShouldShowContentWhenIndicatorStringIsContainedInAnswerOfTypeString() { - val html = "@contains('link-a','basket')

        Text

        @contains('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@contains('1234/link-a','basket')

        Text

        @contains('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -391,17 +421,18 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        Text

        ", populatedHtml) } @Test fun testProcessContainsShouldShowContentWhenIndicatorIntegerMatchesAnswerOfTypeInteger() { - val html = "@contains('link-a','10')

        Text

        @contains('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@contains('1234/link-a','10')

        Text

        @contains('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -410,17 +441,18 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        Text

        ", populatedHtml) } @Test fun testProcessContainsShouldShowContentWhenIndicatorDecimalMatchesAnswerOfTypeDecimal() { - val html = "@contains('link-a','1.5')

        Text

        @contains('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@contains('1234/link-a','1.5')

        Text

        @contains('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -429,17 +461,18 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        Text

        ", populatedHtml) } @Test fun testProcessContainsShouldShowContentWhenIndicatorBooleanMatchesAnswerOfTypeBoolean() { - val html = "@contains('link-a','true')

        Text

        @contains('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@contains('1234/link-a','true')

        Text

        @contains('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -448,17 +481,18 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        Text

        ", populatedHtml) } @Test fun testProcessContainsShouldShowContentWhenIndicatorQuantityMatchesAnswerOfTypeQuantity() { - val html = "@contains('link-a','3 years')

        Text

        @contains('link-a')" - val questionnaireResponse = - QuestionnaireResponse().apply { + val html = "@contains('1234/link-a','3 years')

        Text

        @contains('1234/link-a')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -469,19 +503,20 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        Text

        ", populatedHtml) } @Test fun testProcessContainsShouldShowContentWhenIndicatorDateMatchesAnswerOfTypeDate() { - val html = "@contains('link-a','14-May-2024')

        Text

        @contains('link-a')" + val html = "@contains('1234/link-a','14-May-2024')

        Text

        @contains('1234/link-a')" val calendar = Calendar.getInstance().apply { set(2024, Calendar.MAY, 14) } val specificDate: Date = calendar.time - val questionnaireResponse = - QuestionnaireResponse().apply { + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -492,9 +527,31 @@ class HtmlPopulatorTest { ) } } - } - val htmlPopulator = HtmlPopulator(questionnaireResponse) + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) + val populatedHtml = htmlPopulator.populateHtml(html) + Assert.assertEquals("

        Text

        ", populatedHtml) + } + + @Test + fun testProcessIsQuestionnaireSubmittedShouldShowContentWhenTheRelatedQuestionnaireResponseExists() { + val html = "@is-questionnaire-submitted('q-1234')

        Text

        @is-questionnaire-submitted('q-1234')" + val questionnaireResponses = + listOf(QuestionnaireResponse().apply { + id = "1234" + questionnaire = "Questionnaire/q-1234" + }) + val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

        Text

        ", populatedHtml) } + + @Test + fun testProcessIsQuestionnaireSubmittedShouldNotShowContentWhenTheRelatedQuestionnaireResponseDoesNotExists() { + val html = "@is-questionnaire-submitted('q-1234')

        Text

        @is-questionnaire-submitted('q-1234')" + val questionnaireResponses = listOf() + val htmlPopulator = HtmlPopulator(questionnaireResponses) + val populatedHtml = htmlPopulator.populateHtml(html) + Assert.assertEquals("", populatedHtml) + } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt index c0f5bc31c5..3f27b418f4 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt @@ -54,21 +54,27 @@ class PdfLauncherFragment : DialogFragment() { val pdfConfig = getPdfConfig() - val questionnaireId = questionnaireConfig.id.extractLogicalIdUuid() - val subjectId = questionnaireConfig.resourceIdentifier!!.extractLogicalIdUuid() - val subjectType = questionnaireConfig.resourceType!! - val htmlBinaryId = questionnaireConfig.htmlBinaryId!!.extractLogicalIdUuid() - val htmlTitle = questionnaireConfig.htmlTitle ?: getString(R.string.default_html_title) + val pdfStructureId = pdfConfig.pdfStructureReference!!.extractLogicalIdUuid() + val pdfTitle = StringBuilder().append(pdfConfig.pdfTitle ?: getString(R.string.default_html_title)) + val pdfTitleSuffix = pdfConfig.pdfTitleSuffix + val subjectReference = pdfConfig.subjectReference!!.extractLogicalIdUuid() + val subjectType = pdfConfig.subjectType!! + val questionnaireIds = pdfConfig.questionnaireReferences.map { it.extractLogicalIdUuid() } ?: emptyList() lifecycleScope.launch(Dispatchers.IO) { - val questionnaireResponse = - pdfLauncherViewModel.retrieveQuestionnaireResponse( - questionnaireId, - subjectId, - subjectType, - ) - val htmlBinary = pdfLauncherViewModel.retrieveBinary(htmlBinaryId) - generatePdf(questionnaireResponse, htmlBinary, htmlTitle) + val questionnaireResponses = + questionnaireIds.mapNotNull { questionnaireId -> + pdfLauncherViewModel.retrieveQuestionnaireResponse( + questionnaireId, + subjectReference, + subjectType, + ) + } + val htmlBinary = pdfLauncherViewModel.retrieveBinary(pdfStructureId) + + if (pdfTitleSuffix != null) pdfTitle.append(" - $pdfTitleSuffix") + + generatePdf(questionnaireResponses, htmlBinary, pdfTitle.toString()) } } @@ -88,22 +94,22 @@ class PdfLauncherFragment : DialogFragment() { /** * Generates a PDF using the provided questionnaire response and HTML template. * - * @param questionnaireResponse the [QuestionnaireResponse] object containing user responses. + * @param questionnaireResponses containing user responses. * @param htmlBinary the [Binary] object containing the HTML template. * @param htmlTitle the title to be used for the generated PDF. */ private suspend fun generatePdf( - questionnaireResponse: QuestionnaireResponse?, + questionnaireResponses: List, htmlBinary: Binary?, htmlTitle: String, ) { - if (questionnaireResponse == null || htmlBinary == null) { + if (questionnaireResponses.isEmpty() || htmlBinary == null) { dismiss() return } val htmlContent = htmlBinary.content.decodeToString() - val populatedHtml = HtmlPopulator(questionnaireResponse).populateHtml(htmlContent) + val populatedHtml = HtmlPopulator(questionnaireResponses).populateHtml(htmlContent) withContext(Dispatchers.Main) { pdfGenerator.generatePdfWithHtml(populatedHtml, htmlTitle) { dismiss() } From dd426460c52f024915b73bfb3974e8bd933aa544 Mon Sep 17 00:00:00 2001 From: fikrimilano Date: Fri, 13 Sep 2024 08:24:42 +0700 Subject: [PATCH 3/7] Add new tag to check if Questionnaire has been submitted --- .../fhircore/engine/pdf/HtmlPopulator.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt index 7a3232e544..4d51b15d18 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt @@ -95,6 +95,10 @@ class HtmlPopulator( val matcher = containsPattern.matcher(html.substring(i)) if (matcher.find()) processContains(i, html, matcher) else i++ } + html.startsWith("@is-questionnaire-submitted", i) -> { + val matcher = isQuestionnaireSubmittedPattern.matcher(html.substring(i)) + if (matcher.find()) processIsQuestionnaireSubmitted(i, html, matcher) else i++ + } else -> i++ } } @@ -215,6 +219,25 @@ class HtmlPopulator( } } + /** + * Processes the @is-questionnaire-submitted tag by checking if the corresponding [QuestionnaireResponse] exists. + * Replaces the tag with the content if the indicator is true, otherwise removes the tag. + * + * @param i The starting index of the tag in the HTML. + * @param html The StringBuilder containing the HTML. + * @param matcher The Matcher object for the regex pattern. + */ + private fun processIsQuestionnaireSubmitted(i: Int, html: StringBuilder, matcher: Matcher) { + val id = matcher.group(1) + val content = matcher.group(2) ?: "" + val doesQuestionnaireExists = questionnaireIds.contains(id) + if (doesQuestionnaireExists) { + html.replace(i, matcher.end() + i, content) + } else { + html.replace(i, matcher.end() + i, "") + } + } + companion object { // Compile regex patterns for different tags private val isNotEmptyPattern = @@ -224,5 +247,7 @@ class HtmlPopulator( private val submittedDatePattern = Pattern.compile("@submitted-date\\('([^']+)'(?:,'([^']+)')?\\)") private val containsPattern = Pattern.compile("@contains\\('([^']+)','([^']+)'\\)((?s).*?)@contains\\('\\1'\\)") + private val isQuestionnaireSubmittedPattern = + Pattern.compile("@is-questionnaire-submitted\\('([^']+)'\\)((?s).*?)@is-questionnaire-submitted\\('\\1'\\)") } } From 1bea4400c32deae06b7338490ca4d4ea4c6cf65b Mon Sep 17 00:00:00 2001 From: fikrimilano Date: Fri, 13 Sep 2024 08:44:11 +0700 Subject: [PATCH 4/7] Remove subjectType since subjectReference is used --- .../engine/configuration/PdfConfig.kt | 1 - .../quest/ui/pdf/PdfLauncherFragment.kt | 4 +--- .../quest/ui/pdf/PdfLauncherViewModel.kt | 16 ++++++--------- .../quest/ui/pdf/PdfLauncherFragmentTest.kt | 20 ++++++++----------- 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt index 9f9b5f6e45..d778727a04 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt @@ -29,7 +29,6 @@ data class PdfConfig( val pdfTitleSuffix: String? = null, val pdfStructureReference: String? = null, val subjectReference: String? = null, - val subjectType: ResourceType? = null, val questionnaireReferences: List = emptyList() ) : java.io.Serializable, Parcelable { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt index 3f27b418f4..be76c869da 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt @@ -57,8 +57,7 @@ class PdfLauncherFragment : DialogFragment() { val pdfStructureId = pdfConfig.pdfStructureReference!!.extractLogicalIdUuid() val pdfTitle = StringBuilder().append(pdfConfig.pdfTitle ?: getString(R.string.default_html_title)) val pdfTitleSuffix = pdfConfig.pdfTitleSuffix - val subjectReference = pdfConfig.subjectReference!!.extractLogicalIdUuid() - val subjectType = pdfConfig.subjectType!! + val subjectReference = pdfConfig.subjectReference!! val questionnaireIds = pdfConfig.questionnaireReferences.map { it.extractLogicalIdUuid() } ?: emptyList() lifecycleScope.launch(Dispatchers.IO) { @@ -67,7 +66,6 @@ class PdfLauncherFragment : DialogFragment() { pdfLauncherViewModel.retrieveQuestionnaireResponse( questionnaireId, subjectReference, - subjectType, ) } val htmlBinary = pdfLauncherViewModel.retrieveBinary(pdfStructureId) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherViewModel.kt index b82b1488d3..b5f3384c6a 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherViewModel.kt @@ -44,17 +44,15 @@ constructor( * Retrieve the [QuestionnaireResponse] for the given questionnaire and subject. * * @param questionnaireId The ID of the questionnaire. - * @param subjectId The ID of the subject. - * @param subjectType The type of the subject (resource type). + * @param subjectReference The ID of the subject. * @return The [QuestionnaireResponse] if found, otherwise null. */ suspend fun retrieveQuestionnaireResponse( questionnaireId: String, - subjectId: String, - subjectType: ResourceType, + subjectReference: String, ): QuestionnaireResponse? { val searchQuery = - createQuestionnaireResponseSearchQuery(questionnaireId, subjectId, subjectType) + createQuestionnaireResponseSearchQuery(questionnaireId, subjectReference) return defaultRepository.search(searchQuery).firstOrNull() } @@ -62,17 +60,15 @@ constructor( * Create a search query for [QuestionnaireResponse]. * * @param questionnaireId The ID of the questionnaire. - * @param subjectId The ID of the subject. - * @param subjectType The type of the subject (resource type). + * @param subjectReference The ID of the subject. * @return The search query for [QuestionnaireResponse]. */ private fun createQuestionnaireResponseSearchQuery( questionnaireId: String, - subjectId: String, - subjectType: ResourceType, + subjectReference: String, ): Search { return Search(ResourceType.QuestionnaireResponse).apply { - filter(QuestionnaireResponse.SUBJECT, { value = "$subjectType/$subjectId" }) + filter(QuestionnaireResponse.SUBJECT, { value = subjectReference }) filter( QuestionnaireResponse.QUESTIONNAIRE, { value = "${ResourceType.Questionnaire}/$questionnaireId" }, diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt index f9c7151057..ec95efe3b3 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt @@ -56,7 +56,7 @@ class PdfLauncherFragmentTest : RobolectricTest() { val questionnaireResponse = QuestionnaireResponse().apply { questionnaire = "Questionnaire/id" } val htmlBinary = Binary().apply { content = "mock content".toByteArray() } - coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } returns + coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any()) } returns questionnaireResponse coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary @@ -66,7 +66,6 @@ class PdfLauncherFragmentTest : RobolectricTest() { pdfTitleSuffix = "suffix", pdfStructureReference = "Binary/id", subjectReference = "Patient/id", - subjectType = ResourceType.Patient, questionnaireReferences = listOf("QuestionnaireResponse/id") ) .encodeJson() @@ -86,7 +85,7 @@ class PdfLauncherFragmentTest : RobolectricTest() { activity.supportFragmentManager.beginTransaction().add(fragment, null).commitNow() - coVerify { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } + coVerify { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any()) } coVerify { pdfLauncherViewModel.retrieveBinary(any()) } verify { pdfGenerator.generatePdfWithHtml(any(), any(), any()) } } @@ -96,7 +95,7 @@ class PdfLauncherFragmentTest : RobolectricTest() { val questionnaireResponse: QuestionnaireResponse? = null val htmlBinary = Binary().apply { content = "mock content".toByteArray() } - coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } returns + coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any()) } returns questionnaireResponse coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary @@ -106,7 +105,6 @@ class PdfLauncherFragmentTest : RobolectricTest() { pdfTitleSuffix = "suffix", pdfStructureReference = "Binary/id", subjectReference = "Patient/id", - subjectType = ResourceType.Patient, questionnaireReferences = listOf("QuestionnaireResponse/id") ) .encodeJson() @@ -126,7 +124,7 @@ class PdfLauncherFragmentTest : RobolectricTest() { activity.supportFragmentManager.beginTransaction().add(fragment, null).commitNow() - coVerify { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } + coVerify { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any()) } coVerify { pdfLauncherViewModel.retrieveBinary(any()) } verify(inverse = true) { pdfGenerator.generatePdfWithHtml(any(), any(), any()) } } @@ -136,7 +134,7 @@ class PdfLauncherFragmentTest : RobolectricTest() { val questionnaireResponse = QuestionnaireResponse() val htmlBinary: Binary? = null - coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } returns + coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any()) } returns questionnaireResponse coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary @@ -146,7 +144,6 @@ class PdfLauncherFragmentTest : RobolectricTest() { pdfTitleSuffix = "suffix", pdfStructureReference = "Binary/id", subjectReference = "Patient/id", - subjectType = ResourceType.Patient, questionnaireReferences = listOf("QuestionnaireResponse/id") ) .encodeJson() @@ -166,7 +163,7 @@ class PdfLauncherFragmentTest : RobolectricTest() { activity.supportFragmentManager.beginTransaction().add(fragment, null).commitNow() - coVerify { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } + coVerify { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any()) } coVerify { pdfLauncherViewModel.retrieveBinary(any()) } verify(inverse = true) { pdfGenerator.generatePdfWithHtml(any(), any(), any()) } } @@ -176,7 +173,7 @@ class PdfLauncherFragmentTest : RobolectricTest() { val questionnaireResponse: QuestionnaireResponse? = null val htmlBinary: Binary? = null - coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } returns + coEvery { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any()) } returns questionnaireResponse coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary @@ -186,7 +183,6 @@ class PdfLauncherFragmentTest : RobolectricTest() { pdfTitleSuffix = "suffix", pdfStructureReference = "Binary/id", subjectReference = "Patient/id", - subjectType = ResourceType.Patient, questionnaireReferences = listOf("QuestionnaireResponse/id") ) .encodeJson() @@ -206,7 +202,7 @@ class PdfLauncherFragmentTest : RobolectricTest() { activity.supportFragmentManager.beginTransaction().add(fragment, null).commitNow() - coVerify { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any(), any()) } + coVerify { pdfLauncherViewModel.retrieveQuestionnaireResponse(any(), any()) } coVerify { pdfLauncherViewModel.retrieveBinary(any()) } verify(inverse = true) { pdfGenerator.generatePdfWithHtml(any(), any(), any()) } } From c9ae8fbf352e1a57ff5d05fd980ea18ada270eac Mon Sep 17 00:00:00 2001 From: fikrimilano Date: Fri, 13 Sep 2024 08:46:28 +0700 Subject: [PATCH 5/7] Fix test and spotless --- .../engine/configuration/PdfConfig.kt | 3 +- .../fhircore/engine/pdf/HtmlPopulator.kt | 43 +- .../fhircore/engine/pdf/HtmlPopulatorTest.kt | 493 +++++++++++------- .../quest/ui/pdf/PdfLauncherFragment.kt | 10 +- .../quest/ui/pdf/PdfLauncherViewModel.kt | 3 +- .../quest/util/extensions/ConfigExtensions.kt | 8 +- .../quest/ui/pdf/PdfLauncherFragmentTest.kt | 29 +- 7 files changed, 349 insertions(+), 240 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt index d778727a04..b17e97f247 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt @@ -19,7 +19,6 @@ package org.smartregister.fhircore.engine.configuration import android.os.Parcelable import kotlinx.parcelize.Parcelize import kotlinx.serialization.Serializable -import org.hl7.fhir.r4.model.ResourceType import org.smartregister.fhircore.engine.util.extension.interpolate @Serializable @@ -29,7 +28,7 @@ data class PdfConfig( val pdfTitleSuffix: String? = null, val pdfStructureReference: String? = null, val subjectReference: String? = null, - val questionnaireReferences: List = emptyList() + val questionnaireReferences: List = emptyList(), ) : java.io.Serializable, Parcelable { fun interpolate(computedValuesMap: Map) = diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt index 4d51b15d18..f85d38b9f2 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt @@ -16,31 +16,33 @@ package org.smartregister.fhircore.engine.pdf +import java.util.Date import java.util.regex.Matcher import java.util.regex.Pattern import org.hl7.fhir.r4.model.BaseDateTimeType import org.hl7.fhir.r4.model.QuestionnaireResponse -import org.smartregister.fhircore.engine.util.extension.allItems import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent +import org.smartregister.fhircore.engine.util.extension.allItems import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid import org.smartregister.fhircore.engine.util.extension.formatDate import org.smartregister.fhircore.engine.util.extension.makeItReadable import org.smartregister.fhircore.engine.util.extension.valueToString -import java.util.Date /** * HtmlPopulator class is responsible for processing an HTML template by replacing custom tags with * data from QuestionnaireResponses. The class uses various regex patterns to find and replace - * custom tags such as @is-not-empty, @answer-as-list, @answer, @submitted-date, @contains, and - * @is-questionnaire-submitted. + * custom tags such as @is-not-empty, @answer-as-list, @answer, @submitted-date, @contains, + * and @is-questionnaire-submitted. * - * @property questionnaireResponses The QuestionnaireResponses object containing data for replacement. + * @property questionnaireResponses The QuestionnaireResponses object containing data for + * replacement. */ class HtmlPopulator( questionnaireResponses: List, ) { // Store answers as key-value pairs with link-id as key - private val answerMap: MutableMap> = mutableMapOf() + private val answerMap: MutableMap> = + mutableMapOf() // Store submitted-date of a questionnaire response with link-id as key private val submittedDateMap: MutableMap = mutableMapOf() @@ -51,10 +53,11 @@ class HtmlPopulator( init { questionnaireResponses.forEach { questionnaireResponse -> val questionnaireId = questionnaireResponse.questionnaire.extractLogicalIdUuid() - val answerMap = questionnaireResponse.allItems.associateBy( - keySelector = { "$questionnaireId/${it.linkId}" }, - valueTransform = { it.answer }, - ) + val answerMap = + questionnaireResponse.allItems.associateBy( + keySelector = { "$questionnaireId/${it.linkId}" }, + valueTransform = { it.answer }, + ) this.answerMap.putAll(answerMap) this.submittedDateMap[questionnaireId] = questionnaireResponse.meta.lastUpdated ?: Date() this.questionnaireIds.add(questionnaireId) @@ -141,8 +144,7 @@ class HtmlPopulator( private fun processAnswerAsList(i: Int, html: StringBuilder, matcher: Matcher) { val linkId = matcher.group(1) val answerAsList = - answerMap.getOrDefault(linkId, listOf()).joinToString(separator = "") { - answer -> + answerMap.getOrDefault(linkId, listOf()).joinToString(separator = "") { answer -> "
      • ${answer.value.valueToString()}
      • " } html.replace(i, matcher.end() + i, answerAsList) @@ -160,8 +162,9 @@ class HtmlPopulator( val dateFormat = matcher.group(2) val answer = answerMap.getOrDefault(linkId, listOf()).joinToString { answer -> - if (dateFormat == null) answer.value.valueToString() - else answer.value.valueToString(dateFormat) + if (dateFormat == null) { + answer.value.valueToString() + } else answer.value.valueToString(dateFormat) } html.replace(i, matcher.end() + i, answer) } @@ -220,8 +223,9 @@ class HtmlPopulator( } /** - * Processes the @is-questionnaire-submitted tag by checking if the corresponding [QuestionnaireResponse] exists. - * Replaces the tag with the content if the indicator is true, otherwise removes the tag. + * Processes the @is-questionnaire-submitted tag by checking if the corresponding + * [QuestionnaireResponse] exists. Replaces the tag with the content if the indicator is true, + * otherwise removes the tag. * * @param i The starting index of the tag in the HTML. * @param html The StringBuilder containing the HTML. @@ -244,10 +248,13 @@ class HtmlPopulator( Pattern.compile("@is-not-empty\\('([^']+)'\\)((?s).*?)@is-not-empty\\('\\1'\\)") private val answerAsListPattern = Pattern.compile("@answer-as-list\\('([^']+)'\\)") private val answerPattern = Pattern.compile("@answer\\('([^']+)'(?:,'([^']+)')?\\)") - private val submittedDatePattern = Pattern.compile("@submitted-date\\('([^']+)'(?:,'([^']+)')?\\)") + private val submittedDatePattern = + Pattern.compile("@submitted-date\\('([^']+)'(?:,'([^']+)')?\\)") private val containsPattern = Pattern.compile("@contains\\('([^']+)','([^']+)'\\)((?s).*?)@contains\\('\\1'\\)") private val isQuestionnaireSubmittedPattern = - Pattern.compile("@is-questionnaire-submitted\\('([^']+)'\\)((?s).*?)@is-questionnaire-submitted\\('\\1'\\)") + Pattern.compile( + "@is-questionnaire-submitted\\('([^']+)'\\)((?s).*?)@is-questionnaire-submitted\\('\\1'\\)", + ) } } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/pdf/HtmlPopulatorTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/pdf/HtmlPopulatorTest.kt index 74b6ba801c..df51ac3af6 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/pdf/HtmlPopulatorTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/pdf/HtmlPopulatorTest.kt @@ -40,6 +40,7 @@ class HtmlPopulatorTest { listOf( QuestionnaireResponse().apply { id = "1234" + questionnaire = "Questionnaire/1234" addItem().apply { linkId = "link-a" answer = buildList { @@ -50,7 +51,7 @@ class HtmlPopulatorTest { ) } } - } + }, ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) @@ -64,11 +65,12 @@ class HtmlPopulatorTest { listOf( QuestionnaireResponse().apply { id = "1234" + questionnaire = "Questionnaire/1234" addItem().apply { linkId = "link-a" answer = emptyList() } - } + }, ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) @@ -82,7 +84,9 @@ class HtmlPopulatorTest { listOf( QuestionnaireResponse().apply { id = "1234" - addItem().apply { linkId = "link-a" } } + questionnaire = "Questionnaire/1234" + addItem().apply { linkId = "link-a" } + }, ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) @@ -92,7 +96,13 @@ class HtmlPopulatorTest { @Test fun testIsNotEmptyShouldHideContentWhenLinkIdNotExistInQR() { val html = "@is-not-empty('1234/link-a')

        Text

        @is-not-empty('1234/link-a')" - val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val questionnaireResponses = + listOf( + QuestionnaireResponse().apply { + id = "1234" + questionnaire = "Questionnaire/1234" + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) @@ -101,16 +111,31 @@ class HtmlPopulatorTest { @Test fun testIsNotEmptyShouldShowMalformedTagAndContentIfLinkIdOfBothTagDoesNotMatch() { val html = "@is-not-empty('1234/link-a')

        Text

        @is-not-empty('1234/link-b')" - val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val questionnaireResponses = + listOf( + QuestionnaireResponse().apply { + id = "1234" + questionnaire = "Questionnaire/1234" + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) - Assert.assertEquals("@is-not-empty('1234/link-a')

        Text

        @is-not-empty('1234/link-b')", populatedHtml) + Assert.assertEquals( + "@is-not-empty('1234/link-a')

        Text

        @is-not-empty('1234/link-b')", + populatedHtml, + ) } @Test fun testIsNotEmptyShouldShowMalformedTagAndContentIfOnly1TagExist() { val html = "@is-not-empty('1234/link-a')

        Text

        " - val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val questionnaireResponses = + listOf( + QuestionnaireResponse().apply { + id = "1234" + questionnaire = "Questionnaire/1234" + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("@is-not-empty('1234/link-a')

        Text

        ", populatedHtml) @@ -118,21 +143,25 @@ class HtmlPopulatorTest { @Test fun testIsNotEmptyShouldShowContentAndNestedMalformedTagIfAnswerOfRootTagExist() { - val html = "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

        Text

        @is-not-empty('1234/link-a')" + val html = + "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

        Text

        @is-not-empty('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 1", "code 1", "display 1") - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 1", "code 1", "display 1") + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("@is-not-empty('1234/link-b')

        Text

        ", populatedHtml) @@ -140,11 +169,16 @@ class HtmlPopulatorTest { @Test fun testIsNotEmptyShouldHideContentAndNestedMalformedTagIfAnswerOfRootTagIsNotExist() { - val html = "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

        Text

        @is-not-empty('1234/link-a')" + val html = + "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

        Text

        @is-not-empty('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { linkId = "link-a" } }) + questionnaire = "Questionnaire/1234" + addItem().apply { linkId = "link-a" } + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) @@ -152,15 +186,19 @@ class HtmlPopulatorTest { @Test fun testIsNotEmptyShouldHideContentAndNestedMalformedTagIfAnswerOfRootTagIsEmpty() { - val html = "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

        Text

        @is-not-empty('1234/link-a')" + val html = + "@is-not-empty('1234/link-a')@is-not-empty('1234/link-b')

        Text

        @is-not-empty('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = emptyList() - } - }) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = emptyList() + } + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) @@ -170,19 +208,22 @@ class HtmlPopulatorTest { fun testIsNotEmptyShouldShowEmptyContentIfAnswerExist() { val html = "@is-not-empty('1234/link-a')@is-not-empty('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 1", "code 1", "display 1") - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 1", "code 1", "display 1") + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) @@ -192,24 +233,27 @@ class HtmlPopulatorTest { fun testProcessAnswerAsListShouldShowAnswerAsListWhenAnswerExistInQR() { val html = "
          @answer-as-list('1234/link-a')
        " val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 1", "code 1", "display 1") - }, - ) - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 2", "code 2", "display 2") - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 1", "code 1", "display 1") + }, + ) + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 2", "code 2", "display 2") + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("
        • display 1
        • display 2
        ", populatedHtml) @@ -219,9 +263,13 @@ class HtmlPopulatorTest { fun testProcessAnswerAsListShouldShowEmptyAnswerAsListWhenAnswerNotExistInQR() { val html = "
          @answer-as-list('1234/link-a')
        " val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { linkId = "link-a" } }) + questionnaire = "Questionnaire/1234" + addItem().apply { linkId = "link-a" } + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("
          ", populatedHtml) @@ -230,7 +278,13 @@ class HtmlPopulatorTest { @Test fun testProcessAnswerAsListShouldShowEmptyAnswerAsListWhenLinkIdNotExistInQR() { val html = "
            @answer-as-list('1234/link-a')
          " - val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val questionnaireResponses = + listOf( + QuestionnaireResponse().apply { + id = "1234" + questionnaire = "Questionnaire/1234" + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("
            ", populatedHtml) @@ -240,17 +294,20 @@ class HtmlPopulatorTest { fun testProcessAnswerShouldShowAnswerWhenAnswerExistInQR() { val html = "

            @answer('1234/link-a')

            " val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { value = StringType("string 1") }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { value = StringType("string 1") }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            string 1

            ", populatedHtml) @@ -260,9 +317,13 @@ class HtmlPopulatorTest { fun testProcessAnswerShouldShowEmptyAnswerWhenAnswerNotExistInQR() { val html = "

            @answer('1234/link-a')

            " val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { linkId = "link-a" } }) + questionnaire = "Questionnaire/1234" + addItem().apply { linkId = "link-a" } + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            ", populatedHtml) @@ -271,7 +332,13 @@ class HtmlPopulatorTest { @Test fun testProcessAnswerShouldShowEmptyAnswerWhenLinkIdNotExistInQR() { val html = "

            @answer('1234/link-a')

            " - val questionnaireResponses = listOf(QuestionnaireResponse().apply { id = "1234" }) + val questionnaireResponses = + listOf( + QuestionnaireResponse().apply { + id = "1234" + questionnaire = "Questionnaire/1234" + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            ", populatedHtml) @@ -283,19 +350,22 @@ class HtmlPopulatorTest { val specificDate: Date = calendar.time val html = "

            @answer('1234/link-a')

            " val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = DateTimeType(specificDate) - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = DateTimeType(specificDate) + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            14-May-2024

            ", populatedHtml) @@ -307,19 +377,22 @@ class HtmlPopulatorTest { val specificDate: Date = calendar.time val html = "

            @answer('1234/link-a','MMMM d, yyyy')

            " val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = DateTimeType(specificDate) - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = DateTimeType(specificDate) + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            May 14, 2024

            ", populatedHtml) @@ -331,9 +404,13 @@ class HtmlPopulatorTest { val specificDate: Date = calendar.time val html = "

            @submitted-date('1234')

            " val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - meta = Meta().apply { lastUpdated = specificDate } }) + questionnaire = "Questionnaire/1234" + meta = Meta().apply { lastUpdated = specificDate } + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            14-May-2024

            ", populatedHtml) @@ -345,9 +422,13 @@ class HtmlPopulatorTest { val specificDate: Date = calendar.time val html = "

            @submitted-date('1234','MMMM d, yyyy')

            " val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - meta = Meta().apply { lastUpdated = specificDate } }) + questionnaire = "Questionnaire/1234" + meta = Meta().apply { lastUpdated = specificDate } + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            May 14, 2024

            ", populatedHtml) @@ -357,24 +438,27 @@ class HtmlPopulatorTest { fun testProcessContainsShouldShowContentWhenIndicatorCodeMatchesWithAnswerOfTypeCoding() { val html = "@contains('1234/link-a','code 2')

            Text

            @contains('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 1", "code 1", "display 1") - }, - ) - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 2", "code 2", "display 2") - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 1", "code 1", "display 1") + }, + ) + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 2", "code 2", "display 2") + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            Text

            ", populatedHtml) @@ -384,24 +468,27 @@ class HtmlPopulatorTest { fun testProcessContainsShouldHideContentWhenIndicatorCodeDoesNotMatchWithAnswerOfTypeCoding() { val html = "@contains('1234/link-a','code 3')

            Text

            @contains('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 1", "code 1", "display 1") - }, - ) - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Coding("system 2", "code 2", "display 2") - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 1", "code 1", "display 1") + }, + ) + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Coding("system 2", "code 2", "display 2") + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("", populatedHtml) @@ -411,17 +498,22 @@ class HtmlPopulatorTest { fun testProcessContainsShouldShowContentWhenIndicatorStringIsContainedInAnswerOfTypeString() { val html = "@contains('1234/link-a','basket')

            Text

            @contains('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { value = StringType("basketball") }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = StringType("basketball") + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            Text

            ", populatedHtml) @@ -431,17 +523,20 @@ class HtmlPopulatorTest { fun testProcessContainsShouldShowContentWhenIndicatorIntegerMatchesAnswerOfTypeInteger() { val html = "@contains('1234/link-a','10')

            Text

            @contains('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { value = IntegerType("10") }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { value = IntegerType("10") }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            Text

            ", populatedHtml) @@ -451,17 +546,20 @@ class HtmlPopulatorTest { fun testProcessContainsShouldShowContentWhenIndicatorDecimalMatchesAnswerOfTypeDecimal() { val html = "@contains('1234/link-a','1.5')

            Text

            @contains('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { value = DecimalType("1.5") }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { value = DecimalType("1.5") }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            Text

            ", populatedHtml) @@ -471,17 +569,20 @@ class HtmlPopulatorTest { fun testProcessContainsShouldShowContentWhenIndicatorBooleanMatchesAnswerOfTypeBoolean() { val html = "@contains('1234/link-a','true')

            Text

            @contains('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType("true") }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType("true") }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            Text

            ", populatedHtml) @@ -491,19 +592,22 @@ class HtmlPopulatorTest { fun testProcessContainsShouldShowContentWhenIndicatorQuantityMatchesAnswerOfTypeQuantity() { val html = "@contains('1234/link-a','3 years')

            Text

            @contains('1234/link-a')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = Quantity(null, 3, "system", "years", "years") - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = Quantity(null, 3, "system", "years", "years") + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            Text

            ", populatedHtml) @@ -515,19 +619,22 @@ class HtmlPopulatorTest { val calendar = Calendar.getInstance().apply { set(2024, Calendar.MAY, 14) } val specificDate: Date = calendar.time val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" - addItem().apply { - linkId = "link-a" - answer = buildList { - add( - QuestionnaireResponseItemAnswerComponent().apply { - value = DateTimeType(specificDate) - }, - ) + questionnaire = "Questionnaire/1234" + addItem().apply { + linkId = "link-a" + answer = buildList { + add( + QuestionnaireResponseItemAnswerComponent().apply { + value = DateTimeType(specificDate) + }, + ) + } } - } - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            Text

            ", populatedHtml) @@ -535,12 +642,15 @@ class HtmlPopulatorTest { @Test fun testProcessIsQuestionnaireSubmittedShouldShowContentWhenTheRelatedQuestionnaireResponseExists() { - val html = "@is-questionnaire-submitted('q-1234')

            Text

            @is-questionnaire-submitted('q-1234')" + val html = + "@is-questionnaire-submitted('q-1234')

            Text

            @is-questionnaire-submitted('q-1234')" val questionnaireResponses = - listOf(QuestionnaireResponse().apply { + listOf( + QuestionnaireResponse().apply { id = "1234" questionnaire = "Questionnaire/q-1234" - }) + }, + ) val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) Assert.assertEquals("

            Text

            ", populatedHtml) @@ -548,7 +658,8 @@ class HtmlPopulatorTest { @Test fun testProcessIsQuestionnaireSubmittedShouldNotShowContentWhenTheRelatedQuestionnaireResponseDoesNotExists() { - val html = "@is-questionnaire-submitted('q-1234')

            Text

            @is-questionnaire-submitted('q-1234')" + val html = + "@is-questionnaire-submitted('q-1234')

            Text

            @is-questionnaire-submitted('q-1234')" val questionnaireResponses = listOf() val htmlPopulator = HtmlPopulator(questionnaireResponses) val populatedHtml = htmlPopulator.populateHtml(html) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt index be76c869da..e5d706f92b 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt @@ -38,8 +38,8 @@ import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid /** * A fragment for generating and displaying a PDF based on a questionnaire response. * - * This fragment uses the provided [PdfConfig] to retrieve a questionnaire response, - * populate an HTML template with the response data, and generate a PDF. + * This fragment uses the provided [PdfConfig] to retrieve a questionnaire response, populate an + * HTML template with the response data, and generate a PDF. */ @AndroidEntryPoint class PdfLauncherFragment : DialogFragment() { @@ -55,10 +55,12 @@ class PdfLauncherFragment : DialogFragment() { val pdfConfig = getPdfConfig() val pdfStructureId = pdfConfig.pdfStructureReference!!.extractLogicalIdUuid() - val pdfTitle = StringBuilder().append(pdfConfig.pdfTitle ?: getString(R.string.default_html_title)) + val pdfTitle = + StringBuilder().append(pdfConfig.pdfTitle ?: getString(R.string.default_html_title)) val pdfTitleSuffix = pdfConfig.pdfTitleSuffix val subjectReference = pdfConfig.subjectReference!! - val questionnaireIds = pdfConfig.questionnaireReferences.map { it.extractLogicalIdUuid() } ?: emptyList() + val questionnaireIds = + pdfConfig.questionnaireReferences.map { it.extractLogicalIdUuid() } ?: emptyList() lifecycleScope.launch(Dispatchers.IO) { val questionnaireResponses = diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherViewModel.kt index b5f3384c6a..a424d02c17 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherViewModel.kt @@ -51,8 +51,7 @@ constructor( questionnaireId: String, subjectReference: String, ): QuestionnaireResponse? { - val searchQuery = - createQuestionnaireResponseSearchQuery(questionnaireId, subjectReference) + val searchQuery = createQuestionnaireResponseSearchQuery(questionnaireId, subjectReference) return defaultRepository.search(searchQuery).firstOrNull() } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt index f503c09d1a..bf0e893a23 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/ConfigExtensions.kt @@ -225,10 +225,10 @@ fun ActionConfig.handleClickEvent( navController.navigate(MainNavigationScreen.LocationSelector.route, args) } ApplicationWorkflow.LAUNCH_PDF_GENERATION -> { - val pdfConfig = actionConfig.pdfConfig ?: return - val interpolatedPdfConfig = pdfConfig.interpolate(computedValuesMap) - val appCompatActivity = (navController.context as AppCompatActivity) - PdfLauncherFragment.launch(appCompatActivity, interpolatedPdfConfig.encodeJson()) + val pdfConfig = actionConfig.pdfConfig ?: return + val interpolatedPdfConfig = pdfConfig.interpolate(computedValuesMap) + val appCompatActivity = (navController.context as AppCompatActivity) + PdfLauncherFragment.launch(appCompatActivity, interpolatedPdfConfig.encodeJson()) } else -> return } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt index ec95efe3b3..1ece8ddbb3 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt @@ -27,7 +27,6 @@ import io.mockk.verify import kotlinx.coroutines.runBlocking import org.hl7.fhir.r4.model.Binary import org.hl7.fhir.r4.model.QuestionnaireResponse -import org.hl7.fhir.r4.model.ResourceType import org.junit.Before import org.junit.Rule import org.junit.Test @@ -61,19 +60,17 @@ class PdfLauncherFragmentTest : RobolectricTest() { coEvery { pdfLauncherViewModel.retrieveBinary(any()) } returns htmlBinary val pdfConfig = - PdfConfig( + PdfConfig( pdfTitle = "title", pdfTitleSuffix = "suffix", pdfStructureReference = "Binary/id", subjectReference = "Patient/id", - questionnaireReferences = listOf("QuestionnaireResponse/id") + questionnaireReferences = listOf("QuestionnaireResponse/id"), ) .encodeJson() val fragmentArgs = - Bundle().apply { - putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) - } + Bundle().apply { putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) } val activity = Robolectric.buildActivity(HiltTestActivity::class.java).create().resume().get() @@ -102,17 +99,15 @@ class PdfLauncherFragmentTest : RobolectricTest() { val pdfConfig = PdfConfig( pdfTitle = "title", - pdfTitleSuffix = "suffix", + pdfTitleSuffix = "suffix", pdfStructureReference = "Binary/id", subjectReference = "Patient/id", - questionnaireReferences = listOf("QuestionnaireResponse/id") + questionnaireReferences = listOf("QuestionnaireResponse/id"), ) .encodeJson() val fragmentArgs = - Bundle().apply { - putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) - } + Bundle().apply { putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) } val activity = Robolectric.buildActivity(HiltTestActivity::class.java).create().resume().get() @@ -144,14 +139,12 @@ class PdfLauncherFragmentTest : RobolectricTest() { pdfTitleSuffix = "suffix", pdfStructureReference = "Binary/id", subjectReference = "Patient/id", - questionnaireReferences = listOf("QuestionnaireResponse/id") + questionnaireReferences = listOf("QuestionnaireResponse/id"), ) .encodeJson() val fragmentArgs = - Bundle().apply { - putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) - } + Bundle().apply { putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) } val activity = Robolectric.buildActivity(HiltTestActivity::class.java).create().resume().get() @@ -183,14 +176,12 @@ class PdfLauncherFragmentTest : RobolectricTest() { pdfTitleSuffix = "suffix", pdfStructureReference = "Binary/id", subjectReference = "Patient/id", - questionnaireReferences = listOf("QuestionnaireResponse/id") + questionnaireReferences = listOf("QuestionnaireResponse/id"), ) .encodeJson() val fragmentArgs = - Bundle().apply { - putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) - } + Bundle().apply { putString(PdfLauncherFragment.EXTRA_PDF_CONFIG_KEY, pdfConfig) } val activity = Robolectric.buildActivity(HiltTestActivity::class.java).create().resume().get() From bf8f581b57f8ac1c8fdfb3847dc9ee6d011df8e2 Mon Sep 17 00:00:00 2001 From: fikrimilano Date: Tue, 17 Sep 2024 17:16:46 +0700 Subject: [PATCH 6/7] Address review --- .../engine/configuration/PdfConfig.kt | 11 ++--- .../fhircore/engine/pdf/HtmlPopulator.kt | 30 ++++++------- .../quest/ui/pdf/PdfLauncherFragment.kt | 14 +++---- .../quest/ui/pdf/PdfLauncherFragmentTest.kt | 24 +++++------ .../app/configuring/config-types/pdf.mdx | 42 +++++++++++++++++++ .../app/configuring/pdf-generation.mdx | 36 ++++++++++------ 6 files changed, 107 insertions(+), 50 deletions(-) create mode 100644 docs/engineering/app/configuring/config-types/pdf.mdx diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt index b17e97f247..819d95eb85 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/PdfConfig.kt @@ -24,17 +24,18 @@ import org.smartregister.fhircore.engine.util.extension.interpolate @Serializable @Parcelize data class PdfConfig( - val pdfTitle: String? = null, - val pdfTitleSuffix: String? = null, - val pdfStructureReference: String? = null, + val title: String? = null, + val titleSuffix: String? = null, + val structureReference: String? = null, val subjectReference: String? = null, val questionnaireReferences: List = emptyList(), ) : java.io.Serializable, Parcelable { fun interpolate(computedValuesMap: Map) = this.copy( - pdfTitle = pdfTitle?.interpolate(computedValuesMap), - pdfStructureReference = pdfStructureReference?.interpolate(computedValuesMap), + title = title?.interpolate(computedValuesMap), + titleSuffix = titleSuffix?.interpolate(computedValuesMap), + structureReference = structureReference?.interpolate(computedValuesMap), subjectReference = subjectReference?.interpolate(computedValuesMap), questionnaireReferences = questionnaireReferences.map { it.interpolate(computedValuesMap) }, ) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt index f85d38b9f2..4110986616 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/pdf/HtmlPopulator.kt @@ -40,28 +40,30 @@ import org.smartregister.fhircore.engine.util.extension.valueToString class HtmlPopulator( questionnaireResponses: List, ) { - // Store answers as key-value pairs with link-id as key - private val answerMap: MutableMap> = - mutableMapOf() - - // Store submitted-date of a questionnaire response with link-id as key - private val submittedDateMap: MutableMap = mutableMapOf() - - // Store questionnaire-id related to each questionnaire-response - private val questionnaireIds: MutableList = mutableListOf() + private var answerMap: Map> + private var submittedDateMap: Map + private var questionnaireIds: List init { + val answerMap = mutableMapOf>() + val submittedDateMap = mutableMapOf() + val questionnaireIds = mutableListOf() + questionnaireResponses.forEach { questionnaireResponse -> val questionnaireId = questionnaireResponse.questionnaire.extractLogicalIdUuid() - val answerMap = - questionnaireResponse.allItems.associateBy( + questionnaireResponse.allItems + .associateBy( keySelector = { "$questionnaireId/${it.linkId}" }, valueTransform = { it.answer }, ) - this.answerMap.putAll(answerMap) - this.submittedDateMap[questionnaireId] = questionnaireResponse.meta.lastUpdated ?: Date() - this.questionnaireIds.add(questionnaireId) + .let { answerMap.putAll(it) } + submittedDateMap[questionnaireId] = questionnaireResponse.meta.lastUpdated ?: Date() + questionnaireIds.add(questionnaireId) } + + this.answerMap = answerMap + this.submittedDateMap = submittedDateMap + this.questionnaireIds = questionnaireIds } /** diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt index e5d706f92b..07ada38a28 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt @@ -54,10 +54,10 @@ class PdfLauncherFragment : DialogFragment() { val pdfConfig = getPdfConfig() - val pdfStructureId = pdfConfig.pdfStructureReference!!.extractLogicalIdUuid() - val pdfTitle = - StringBuilder().append(pdfConfig.pdfTitle ?: getString(R.string.default_html_title)) - val pdfTitleSuffix = pdfConfig.pdfTitleSuffix + val structureId = pdfConfig.structureReference!!.extractLogicalIdUuid() + val title = + StringBuilder().append(pdfConfig.title ?: getString(R.string.default_html_title)) + val titleSuffix = pdfConfig.titleSuffix val subjectReference = pdfConfig.subjectReference!! val questionnaireIds = pdfConfig.questionnaireReferences.map { it.extractLogicalIdUuid() } ?: emptyList() @@ -70,11 +70,11 @@ class PdfLauncherFragment : DialogFragment() { subjectReference, ) } - val htmlBinary = pdfLauncherViewModel.retrieveBinary(pdfStructureId) + val htmlBinary = pdfLauncherViewModel.retrieveBinary(structureId) - if (pdfTitleSuffix != null) pdfTitle.append(" - $pdfTitleSuffix") + if (titleSuffix != null) title.append(" - $titleSuffix") - generatePdf(questionnaireResponses, htmlBinary, pdfTitle.toString()) + generatePdf(questionnaireResponses, htmlBinary, title.toString()) } } diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt index 1ece8ddbb3..7f797681e4 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragmentTest.kt @@ -61,9 +61,9 @@ class PdfLauncherFragmentTest : RobolectricTest() { val pdfConfig = PdfConfig( - pdfTitle = "title", - pdfTitleSuffix = "suffix", - pdfStructureReference = "Binary/id", + title = "title", + titleSuffix = "suffix", + structureReference = "Binary/id", subjectReference = "Patient/id", questionnaireReferences = listOf("QuestionnaireResponse/id"), ) @@ -98,9 +98,9 @@ class PdfLauncherFragmentTest : RobolectricTest() { val pdfConfig = PdfConfig( - pdfTitle = "title", - pdfTitleSuffix = "suffix", - pdfStructureReference = "Binary/id", + title = "title", + titleSuffix = "suffix", + structureReference = "Binary/id", subjectReference = "Patient/id", questionnaireReferences = listOf("QuestionnaireResponse/id"), ) @@ -135,9 +135,9 @@ class PdfLauncherFragmentTest : RobolectricTest() { val pdfConfig = PdfConfig( - pdfTitle = "title", - pdfTitleSuffix = "suffix", - pdfStructureReference = "Binary/id", + title = "title", + titleSuffix = "suffix", + structureReference = "Binary/id", subjectReference = "Patient/id", questionnaireReferences = listOf("QuestionnaireResponse/id"), ) @@ -172,9 +172,9 @@ class PdfLauncherFragmentTest : RobolectricTest() { val pdfConfig = PdfConfig( - pdfTitle = "title", - pdfTitleSuffix = "suffix", - pdfStructureReference = "Binary/id", + title = "title", + titleSuffix = "suffix", + structureReference = "Binary/id", subjectReference = "Patient/id", questionnaireReferences = listOf("QuestionnaireResponse/id"), ) diff --git a/docs/engineering/app/configuring/config-types/pdf.mdx b/docs/engineering/app/configuring/config-types/pdf.mdx new file mode 100644 index 0000000000..736e66df9e --- /dev/null +++ b/docs/engineering/app/configuring/config-types/pdf.mdx @@ -0,0 +1,42 @@ +--- +title: PDF +--- + +# PDF configuration + +Launch a PDF preview from a defined HTML structure and Questionnaire Responses that contains data to be populated into the PDF. App user then can save that previewed PDF into their device as a file. + +:::info +The [PDF Generation](https://docs.opensrp.io/engineering/app/configuring/pdf-generation) feature is responsible in populating data from Questionnaire Response to HTML. +::: + +## Sample JSON + +```json +{ + "trigger": "ON_CLICK", + "workflow": "LAUNCH_PDF_GENERATION", + "pdfConfig": { + "title": "Depression Report", + "titleSuffix": "James", + "structureReference": "Binary/b1234", + "subjectReference": "Patient/p1234", + "questionnaireReferences": [ + "Questionnaire/q1", + "Questionnaire/q2", + "Questionnaire/q3", + "Questionnaire/q4" + ] + } +} +``` + +## Config properties + +|Property | Description | Required | Default | +|--|--|:--:|:--:| +`title` | The saved PDF file title | No | `null` | +`titleSuffix` | The saved PDF file title suffix; usually contains the subject name | No | `null` | +`structureReference` | The Binary resource that contains HTML structure of the PDF | Yes | `null` | +`subjectReference` | The subject/patient/child who the answers from the Questionnaire Response apply to | Yes | `null` | +`questionnaireReferences` | The Questionnaire resource that is related to a Questionnaire Response which contains data to be populated into the PDF | Yes | `[]` | diff --git a/docs/engineering/app/configuring/pdf-generation.mdx b/docs/engineering/app/configuring/pdf-generation.mdx index 6ce55576b3..8f5d675ed6 100644 --- a/docs/engineering/app/configuring/pdf-generation.mdx +++ b/docs/engineering/app/configuring/pdf-generation.mdx @@ -1,9 +1,11 @@ # PDF Generation ## Overview -The PDF generation feature using the `HtmlPopulator` class simplifies the process of dynamically populating HTML templates with data from a QuestionnaireResponse, making it easy to generate customized content based on user responses. +The PDF generation feature is using the `HtmlPopulator` class that simplifies the process of dynamically populating HTML templates with data from a QuestionnaireResponse, making it easy to generate customized content based on user responses. -The `HtmlPopulator` class is utilized by replacing custom tags with data from a QuestionnaireResponse. It supports tags such as `@is-not-empty`, `@answer-as-list`, `@answer`, `@submitted-date`, and `@contains`. +The `HtmlPopulator` class is utilized by replacing custom tags with data from a QuestionnaireResponse. Currently supported tags are: `@is-not-empty`, `@answer-as-list`, `@answer`, `@submitted-date`, `@contains`, and `@is-questionnaire-submitted`. + +The `HtmlPopulator` class allows multiple QuestionnaireResponses to be populated into the HTML. Please use `questionnaireId/linkId` format to describe which linkId from which Questionnaire you want to retrieve the answer from. ## Usage Below are examples of how each custom tag can be used in an HTML template and the expected output. @@ -12,7 +14,7 @@ Below are examples of how each custom tag can be used in an HTML template and th #### Template HTML: ``` html -

            @is-not-empty('linkId')This content will be included if the answer exists.@is-not-empty('linkId')

            +

            @is-not-empty('questionnaireId/linkId')This content will be included if the answer exists.@is-not-empty('questionnaireId/linkId')

            ``` #### Explanation: @@ -23,7 +25,7 @@ The `@is-not-empty` tag checks if there is an answer for the specified `linkId`. #### Template HTML: ``` html
              - @answer-as-list('linkId') + @answer-as-list('questionnaireId/linkId')
            ``` @@ -34,7 +36,7 @@ The `@answer-as-list` tag will be replaced with a list of answers for the specif #### Template HTML: ``` html -

            The answer is: @answer('linkId')

            +

            The answer is: @answer('questionnaireId/linkId')

            ``` #### Explanation: @@ -44,35 +46,45 @@ The `@answer tag` will be replaced with the answer for the specified `linkId`. I #### Template HTML: ``` html -

            Submitted on: @submitted-date('MM/dd/yyyy')

            +

            Submitted on: @submitted-date('questionnaireId','MM/dd/yyyy')

            ``` #### Explanation: -The `@submitted-date` tag will be replaced with the formatted submission date. If no format is provided, a default date format will be used. +The `@submitted-date` tag will be replaced with the formatted submission date from the specified `questionnaireId`. If no format is provided, a default date format will be used. ### @contains #### Template HTML: ``` html -

            @contains('linkId', 'indicator')This content will be included if the indicator is found.@contains('linkId', 'indicator')

            +

            @contains('questionnaireId/linkId', 'indicator')This content will be included if the indicator is found.@contains('questionnaireId/linkId', 'indicator')

            ``` #### Explanation: The `@contains` tag checks if the specified `linkId` contains the given `indicator`. If the indicator is found, the content within the tags will be included in the final HTML. If the indicator is not found, the content will be removed. +### @is-questionnaire-submitted + +#### Template HTML: +``` html +

            @is-questionnaire-submitted('questionnaireId')This content will only show if the Questionnaire Response of the described Questionnaire exists.@is-questionnaire-submitted('questionnaireId')

            +``` + +#### Explanation: +The `@is-questionnaire-submitted` tag checks if the specified `questionnaireId` has been submitted i.e. the matching Questionnaire Response is passed to the HtmlPopulator class. If the Questionnaire Response is found, the content within the tags will be included in the final HTML. If the indicator is not found, the content will be removed. + ## Example ### Input HTML Template ``` html -

            @is-not-empty('name')Name: @answer('name')@is-not-empty('name')

            +

            @is-not-empty('Q123/name')Name: @answer('Q123/name')@is-not-empty('Q123/name')

            Hobbies:

              - @answer-as-list('hobbies') + @answer-as-list('Q123/hobbies')
            -

            Submitted on: @submitted-date('yyyy-MM-dd')

            -

            @contains('age', '30')This person is 30 years old.@contains('age', '30')

            +

            Submitted on: @submitted-date('Q123','yyyy-MM-dd')

            +

            @contains('Q123/age', '30')This person is 30 years old.@contains('Q123/age', '30')

            ``` From c5aab3a622e81e70d1e70a8f88650ca42c70632c Mon Sep 17 00:00:00 2001 From: fikrimilano Date: Tue, 17 Sep 2024 17:17:48 +0700 Subject: [PATCH 7/7] spotless --- .../smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt index 07ada38a28..6d9d0376b5 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/pdf/PdfLauncherFragment.kt @@ -55,8 +55,7 @@ class PdfLauncherFragment : DialogFragment() { val pdfConfig = getPdfConfig() val structureId = pdfConfig.structureReference!!.extractLogicalIdUuid() - val title = - StringBuilder().append(pdfConfig.title ?: getString(R.string.default_html_title)) + val title = StringBuilder().append(pdfConfig.title ?: getString(R.string.default_html_title)) val titleSuffix = pdfConfig.titleSuffix val subjectReference = pdfConfig.subjectReference!! val questionnaireIds =