From 61ea8e7ff4393280e2e53a1d957389ce9905e725 Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Tue, 12 Nov 2024 13:45:15 +0200 Subject: [PATCH] Really big update (#4) * update * Update FormService.kt * update services * Update local changes --- .../com/google/android/fhir/LocalChange.kt | 12 +- .../org/dtree/fhir/core/fhir/PatchMaker.kt | 100 +++++++++----- .../org/dtree/fhir/core/models/PatientData.kt | 1 + .../fhir/core/uploader/general/FhirClient.kt | 25 +++- .../fhir/server/controller/TasksController.kt | 13 ++ .../plugins/tasks/ChangeAppointmentData.kt | 11 ++ .../dtree/fhir/server/plugins/tasks/Tasks.kt | 5 + .../fhir/server/plugins/tasks/TasksModule.kt | 14 ++ .../fhir/server/services/form/FormService.kt | 128 +++++++++++++++++- .../services/injection/ServicesInjection.kt | 2 + .../fhir/server/services/util/UtilService.kt | 38 ++++++ 11 files changed, 306 insertions(+), 43 deletions(-) create mode 100644 server/src/main/kotlin/org/dtree/fhir/server/services/util/UtilService.kt diff --git a/core/src/main/kotlin/com/google/android/fhir/LocalChange.kt b/core/src/main/kotlin/com/google/android/fhir/LocalChange.kt index 7251072..e8f4e09 100644 --- a/core/src/main/kotlin/com/google/android/fhir/LocalChange.kt +++ b/core/src/main/kotlin/com/google/android/fhir/LocalChange.kt @@ -26,6 +26,7 @@ data class LocalChange( * [LocalChange] class instance is created. */ var token: LocalChangeToken, + val isPatch: Boolean = false, ) { enum class Type(val value: Int) { INSERT(1), // create a new resource. payload is the entire resource json. @@ -38,9 +39,16 @@ data class LocalChange( } } - fun createPatchRequest(iParser: IParser, resource: Resource? = null): BundleEntryComponent { + fun createPatchRequest( + iParser: IParser, + resource: Resource? = null + ): BundleEntryComponent { return if (type == LocalChange.Type.UPDATE) { - createRequest(createPathRequest()) + if (isPatch) { + createRequest(createPathRequest()) + } else { + createRequest(iParser.parseResource(payload) as Resource) + } } else if (resource != null && type == LocalChange.Type.INSERT) { createRequest(resource) } else { diff --git a/core/src/main/kotlin/org/dtree/fhir/core/fhir/PatchMaker.kt b/core/src/main/kotlin/org/dtree/fhir/core/fhir/PatchMaker.kt index ec8f732..65ccf6d 100644 --- a/core/src/main/kotlin/org/dtree/fhir/core/fhir/PatchMaker.kt +++ b/core/src/main/kotlin/org/dtree/fhir/core/fhir/PatchMaker.kt @@ -6,31 +6,39 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.github.fge.jsonpatch.diff.JsonDiff import com.google.android.fhir.LocalChange import com.google.android.fhir.LocalChangeToken +import com.google.android.fhir.sync.upload.patch.PatchOrdering.sccOrderByReferences import org.dtree.fhir.core.utils.logicalId import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent -import org.json.JSONArray import org.hl7.fhir.r4.model.Resource +import org.json.JSONArray import java.time.Instant +import java.util.UUID +import kotlin.random.Random object PatchMaker { + private val resourceHelper = FhirResourceHelper() fun createPatchedRequest( iParser: IParser, resourceMap: Map, resources: List ): List { - return resources.mapNotNull { resource -> - val oldResource = resourceMap[resource.logicalId] - ?: return@mapNotNull LocalChange( - resourceType = resource.fhirType(), - resourceId = resource.logicalId, - versionId = resource.meta.versionId, - timestamp = Instant.now(), - type = LocalChange.Type.INSERT, - payload = iParser.encodeResourceToString(resource.apply { - id = logicalId - }), - token = LocalChangeToken(listOf()) - ).createPatchRequest(iParser, resource) + val changes = resources.mapNotNull { resource -> + val resourceID = resource.logicalId + var oldResource = resourceMap[resourceID] + if (oldResource == null) { + oldResource = resourceMap[resourceID.replace("#", "")] + } + if (oldResource == null) return@mapNotNull LocalChange( + resourceType = resource.fhirType(), + resourceId = resourceID, + versionId = resource.meta.versionId, + timestamp = Instant.now(), + type = LocalChange.Type.INSERT, + payload = iParser.encodeResourceToString(resource.apply { + id = logicalId + }), + token = LocalChangeToken(listOf(Random.nextLong())), + ) val jsonDiff = patch(iParser, oldResource.apply { id = logicalId @@ -42,46 +50,70 @@ object PatchMaker { } else { LocalChange( resourceType = resource.fhirType(), - resourceId = resource.logicalId, + resourceId = resourceID, versionId = resource.meta.versionId, timestamp = Instant.now(), type = LocalChange.Type.UPDATE, - payload = jsonDiff, - token = LocalChangeToken(listOf()) - ).createPatchRequest(iParser) + payload = jsonDiff.second, + token = LocalChangeToken(listOf(Random.nextLong())), + isPatch = jsonDiff.first + ) + } + } + val refs = changes.flatMap { + val resource: Resource = if (it.isPatch) { + resourceMap[it.resourceId]!! + } else { + iParser.parseResource(it.payload) as Resource } + resourceHelper.extractLocalChangeWithRefs(it, resource) } + val ordered = refs.sccOrderByReferences() + val newLocalChanges = ordered.flatMap { it.patchMappings.map { it.localChange } } + return newLocalChanges.map { it.createPatchRequest(iParser) } } - fun patch(iParser: IParser, oldResource: Resource, updatedResource: Resource): String? { - val jsonDiff: JSONArray = diff(iParser, oldResource, updatedResource) + private fun patch(parser: IParser, source: Resource, target: Resource): Pair? { + val objectMapper = ObjectMapper() + val sourceStr = objectMapper.readValue(parser.encodeResourceToString(source), JsonNode::class.java) + val targetStr = objectMapper.readValue(parser.encodeResourceToString(target), JsonNode::class.java) + + val diff = JsonDiff.asJson( + sourceStr, + targetStr, + ) + + val response = getFilteredJSONArray(diff) + val jsonDiff = response.first if (jsonDiff.length() == 0) { println("New resource same as last one") return null } - return jsonDiff.toString() - } - private fun diff(parser: IParser, source: Resource, target: Resource): JSONArray { - val objectMapper = ObjectMapper() - return getFilteredJSONArray( - JsonDiff.asJson( - objectMapper.readValue(parser.encodeResourceToString(source), JsonNode::class.java), - objectMapper.readValue(parser.encodeResourceToString(target), JsonNode::class.java), - ), - ) + return Pair(true, jsonDiff.toString()) } - private fun getFilteredJSONArray(jsonDiff: JsonNode) = - with(JSONArray(jsonDiff.toString())) { - val ignorePaths = setOf("/meta", "/text") + private fun getFilteredJSONArray(jsonDiff: JsonNode): Pair { + var hasMeta = false + val ignorePaths = listOf("/meta", "/text") + val array = with(JSONArray(jsonDiff.toString())) { return@with JSONArray( (0.. - ignorePaths.any { jsonObject.optString("path").startsWith(it) } || jsonObject.optString("op") + val isRemove = jsonObject.optString("op") .equals("remove") + if (jsonObject.optString("path").startsWith("/meta") && !isRemove) { + hasMeta = true + } + var paths = ignorePaths + if (hasMeta && !isRemove) { + paths = listOf("/text") + } + paths.any { jsonObject.optString("path").startsWith(it) } || isRemove }, ) } + return Pair(array, hasMeta) + } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/dtree/fhir/core/models/PatientData.kt b/core/src/main/kotlin/org/dtree/fhir/core/models/PatientData.kt index 42a1435..ae3082b 100644 --- a/core/src/main/kotlin/org/dtree/fhir/core/models/PatientData.kt +++ b/core/src/main/kotlin/org/dtree/fhir/core/models/PatientData.kt @@ -161,6 +161,7 @@ fun Bundle.parsePatientResources(patientId: String): Pair, PatientD tasks = tasks.values.toMutableList(), tracingTasks = tracingTasks, carePlans = activeCarePlans, + currentCarePlan = currentCarePlan, oldCarePlans = allCarePlans.filter { it.status != CarePlan.CarePlanStatus.ACTIVE && it.status != CarePlan.CarePlanStatus.ONHOLD } .toMutableList(), )) diff --git a/core/src/main/kotlin/org/dtree/fhir/core/uploader/general/FhirClient.kt b/core/src/main/kotlin/org/dtree/fhir/core/uploader/general/FhirClient.kt index ae448c0..4635c87 100644 --- a/core/src/main/kotlin/org/dtree/fhir/core/uploader/general/FhirClient.kt +++ b/core/src/main/kotlin/org/dtree/fhir/core/uploader/general/FhirClient.kt @@ -12,15 +12,17 @@ import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Response import okhttp3.logging.HttpLoggingInterceptor +import org.apache.commons.net.ntp.TimeStamp import org.dtree.fhir.core.models.PatientData import org.dtree.fhir.core.models.parsePatientResources import org.dtree.fhir.core.utils.Logger +import org.dtree.fhir.core.utils.createFile import org.dtree.fhir.core.utils.logicalId import org.hl7.fhir.instance.model.api.IBaseBundle import org.hl7.fhir.instance.model.api.IBaseResource import org.hl7.fhir.r4.model.* import java.util.concurrent.TimeUnit - +import kotlin.io.path.Path class FhirClient(private val dotenv: Dotenv, private val iParser: IParser) { val fhirClient: IGenericClient @@ -225,7 +227,8 @@ class FhirClient(private val dotenv: Dotenv, private val iParser: IParser) { if (response is DataResponseState.Success) { Logger.info("Uploaded successfully") } else if (response is DataResponseState.Error) { - throw Exception(response.exception) + saveRemainingData(list.subList(start, list.size - 1)) + throw FailedToUploadException(response.exception.toString()) } } } @@ -270,6 +273,7 @@ class FhirClient(private val dotenv: Dotenv, private val iParser: IParser) { DataResponseState.Success(true) } catch (e: Exception) { Logger.error("Failed to upload batch: ${e.message}") + Logger.info(iParser.encodeResourceToString(bundle)) DataResponseState.Error(e) } } @@ -278,6 +282,19 @@ class FhirClient(private val dotenv: Dotenv, private val iParser: IParser) { private fun exceptionFromResponse(response: Response): Exception { return Exception("Status: ${response.code},message: ${response.message}, body: ${response.body?.string()} ") } + + private fun saveRemainingData(subList: List) { + dotenv["REPORT_DIR"]?.apply { + val time = TimeStamp.getCurrentTime() + val bundle = Bundle() + subList.forEach { + bundle.entry.add(it) + } + iParser.encodeResourceToString(bundle) + .createFile(Path(this).resolve("batch-upload-${time.time}.json").toString()) + } + + } } fun IQuery.paginateExecute(client: FhirClient): List { @@ -292,4 +309,6 @@ fun IQuery.paginateExecute(client: FhirClient): List } return list.toList() as List -} \ No newline at end of file +} + +class FailedToUploadException(message: String) : Exception(message) {} \ No newline at end of file diff --git a/server/src/main/kotlin/org/dtree/fhir/server/controller/TasksController.kt b/server/src/main/kotlin/org/dtree/fhir/server/controller/TasksController.kt index 2765691..b17748a 100644 --- a/server/src/main/kotlin/org/dtree/fhir/server/controller/TasksController.kt +++ b/server/src/main/kotlin/org/dtree/fhir/server/controller/TasksController.kt @@ -1,14 +1,17 @@ package org.dtree.fhir.server.controller import org.dtree.fhir.server.plugins.tasks.ChangeAppointmentData +import org.dtree.fhir.server.plugins.tasks.ChangeStatusType import org.dtree.fhir.server.plugins.tasks.FinishVisitRequest import org.dtree.fhir.server.plugins.tasks.TracingRemovalType import org.dtree.fhir.server.services.form.FormService +import org.dtree.fhir.server.services.util.UtilService import org.koin.core.component.KoinComponent import org.koin.core.component.inject class TasksControllerImpl : TasksController, BaseController(), KoinComponent { private val formService by inject() + private val utilService by inject() override fun finishVisits(finishVisitRequestList: List) { formService.finishVisit(finishVisitRequestList) @@ -18,13 +21,23 @@ class TasksControllerImpl : TasksController, BaseController(), KoinComponent { formService.changeAppointmentData(changeAppointmentDataList) } + override suspend fun changeStatus(patients: List, type: ChangeStatusType) { + formService.changeStatus(patients, type) + } + override suspend fun tracingEnteredInError(patients: List, type: TracingRemovalType) { formService.tracingEnteredInError(patients, type) } + + override suspend fun runUtil() { + utilService.runCli() + } } interface TasksController { fun finishVisits(finishVisitRequestList: List) suspend fun changeAppointmentData(changeAppointmentDataList: List) suspend fun tracingEnteredInError(patients: List, type: TracingRemovalType) + suspend fun runUtil() + suspend fun changeStatus(patients: List, type: ChangeStatusType) } diff --git a/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/ChangeAppointmentData.kt b/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/ChangeAppointmentData.kt index 5aafa65..93af87a 100644 --- a/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/ChangeAppointmentData.kt +++ b/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/ChangeAppointmentData.kt @@ -12,8 +12,19 @@ data class TracingEnteredErrorData( val data: List, ) +data class ChangeStatusData( + val type: ChangeStatusType, + val data: List, +) + enum class TracingRemovalType { EnteredInError, TransferredOut, Deceased +} + +enum class ChangeStatusType { + Discharged, + Deceased, + EnteredInError } \ No newline at end of file diff --git a/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/Tasks.kt b/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/Tasks.kt index 1b72962..ca856d4 100644 --- a/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/Tasks.kt +++ b/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/Tasks.kt @@ -14,5 +14,10 @@ class Tasks() { @Resource("tracing-entered-error") class TracingEnteredError(val parent: Fixes = Fixes()) + + @Resource("change-status") + class ChangeStatus(val parent: Fixes = Fixes()) } + @Resource("util") + class Utils(val parent: Tasks = Tasks()) {} } \ No newline at end of file diff --git a/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/TasksModule.kt b/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/TasksModule.kt index 57b3e83..f8b55c0 100644 --- a/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/TasksModule.kt +++ b/server/src/main/kotlin/org/dtree/fhir/server/plugins/tasks/TasksModule.kt @@ -2,6 +2,7 @@ package org.dtree.fhir.server.plugins.tasks import io.ktor.server.application.* import io.ktor.server.request.* +import io.ktor.server.resources.* import io.ktor.server.resources.post import io.ktor.server.response.* import io.ktor.server.routing.Route @@ -30,4 +31,17 @@ fun Route.tasksModule() { call.respond("Jeff") } + + post { + val body = call.receive() + + controller.changeStatus(body.data, body.type) + + call.respond("Jeff") + } + + get { + controller.runUtil() + call.respond("Jeff") + } } \ No newline at end of file diff --git a/server/src/main/kotlin/org/dtree/fhir/server/services/form/FormService.kt b/server/src/main/kotlin/org/dtree/fhir/server/services/form/FormService.kt index 53d1e53..3aea2c3 100644 --- a/server/src/main/kotlin/org/dtree/fhir/server/services/form/FormService.kt +++ b/server/src/main/kotlin/org/dtree/fhir/server/services/form/FormService.kt @@ -4,11 +4,14 @@ import ca.uhn.fhir.parser.IParser import com.google.android.fhir.datacapture.XFhirQueryResolver import org.apache.commons.lang3.time.DateUtils import org.dtree.fhir.core.fhir.PatchMaker +import org.dtree.fhir.core.models.PatientData import org.dtree.fhir.core.models.TracingType import org.dtree.fhir.core.uploader.general.FhirClient import org.dtree.fhir.core.utils.asYyyyMmDd +import org.dtree.fhir.core.utils.category import org.dtree.fhir.core.utils.createBundleComponent import org.dtree.fhir.server.plugins.tasks.ChangeAppointmentData +import org.dtree.fhir.server.plugins.tasks.ChangeStatusType import org.dtree.fhir.server.plugins.tasks.FinishVisitRequest import org.dtree.fhir.server.plugins.tasks.TracingRemovalType import org.hl7.fhir.r4.model.* @@ -33,6 +36,93 @@ object FormService : KoinComponent { } } + suspend fun changeStatus(patients: List, type: ChangeStatusType) { + val entriesToSave = mutableListOf() + + val structureMapChangeStatus: StructureMap = + fetcher.fetchStructureMap("structure_map/change_status/change_status.map") + val statusQuestionnaire: Questionnaire = + fetcher.getQuestionnaire("questionnaire/profile/change_status.json") + + val structureMapMilestone = + fetcher.fetchStructureMap("structure_map/visit/exposed_infant/milestone_hiv_test/exposed_infant_milestone_hiv_test.map") + val questionnaireMilestone: Questionnaire = + fetcher.getQuestionnaire("questionnaire/visit/exposed_infant/milestone_hiv_test/exposed_infant_milestone_hiv_test.json") + + for (patientId in patients) { + val patientData = client.fetchAllPatientsActiveItems(patientId) + if (patientData.isEmpty()) continue + val questionnaire: Questionnaire + val structureMap: StructureMap + val exposedDischarge = + type == ChangeStatusType.Discharged && patientData.patient.category == "exposed-infant" + if (exposedDischarge) { + questionnaire = questionnaireMilestone.copy() + structureMap = structureMapMilestone + } else { + questionnaire = statusQuestionnaire.copy() + structureMap = structureMapChangeStatus + } + entriesToSave.addAll(handleParse( + questionnaire, + structureMap, + patientData + ) { + if (exposedDischarge) { + updateAnswerInGroup("page-1", "able-to-conduct-test", listOf( + QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { + value = BooleanType(true) + } + )) + updateAnswerInGroup("page-1", "test-type", listOf( + QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { + value = Coding().apply { + code = "dbs" + display = "DBS" + } + } + )) + updateAnswerInGroup("page-1", "are-dbs-test-results-available", listOf( + QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { + value = BooleanType(true) + } + )) + updateAnswerInGroup("page-1", "hiv-test-results", listOf( + QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { + value = Coding().apply { + code = "negative" + display = "Negative" + } + } + )) + updateAnswerInGroup("page-1", "is-child-weaned", listOf( + QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { + value = Coding().apply { + code = "yes" + display = "Yes" + } + } + )) + updateAnswerInGroup("page-1", "when-was-child-weaned", listOf( + QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { + value = Coding().apply { + code = "more-than-6-weeks-ago" + display = "More than 6 weeks ago" + } + } + )) + } else if (type == ChangeStatusType.Discharged) { + updateSingleAnswer("why-are-you-removing-this-person-art-client", Coding().apply { + code = "negative-confirmatory-hiv-test" + display = "Their confirmatory HIV test came back with a negative result" + }) + } + }) + } + + saveResources(entriesToSave) + } + suspend fun changeAppointmentData(body: List) { val structureMap = fetcher.fetchStructureMap("structure_map/profile/patient_edit_profile/patient_edit_profile.map") @@ -46,7 +136,6 @@ object FormService : KoinComponent { if (patientData.isEmpty()) continue var questionnaireResponse = responseGenerator.generateQuestionerResponse(questionnaire, patientData) - if (patientData.currentCarePlan?.period?.end != null && DateUtils.isSameDay( patientData.currentCarePlan?.period?.end, entry.date @@ -72,10 +161,12 @@ object FormService : KoinComponent { questionnaireResponse = responseUpdater.getQuestionnaireResponse() println(iParser.encodeResourceToString(questionnaireResponse)) val bundle = responseGenerator.extractBundle(questionnaire, questionnaireResponse, structureMap) + println(iParser.encodeResourceToString(bundle)) val bundleResources = bundle.entry.map { val resource = it.resource if (resource is Appointment) { - val idx = resource.participant.indexOfFirst { it.actor.reference.contains("Practitioner/Practitioner/") } + val idx = + resource.participant.indexOfFirst { it.actor?.reference?.contains("Practitioner/Practitioner/") == true } if (idx != -1) { resource.participant[idx] = resource.participant[idx].apply { actor.reference = actor.reference.replace("Practitioner/Practitioner/", "Practitioner/") @@ -89,7 +180,6 @@ object FormService : KoinComponent { entriesToSave.add(questionnaireResponse.createBundleComponent()) entriesToSave.addAll(PatchMaker.createPatchedRequest(iParser, patientData.getAllItemMap(), bundleResources)) } - saveResources(entriesToSave) } @@ -110,7 +200,8 @@ object FormService : KoinComponent { val tracingType = patientData.getTracingType() if (tracingType == TracingType.none) continue - val questionnaire = if (tracingType == TracingType.phone) phoneQuestionnaire.copy() else homeQuestionnaire.copy() + val questionnaire = + if (tracingType == TracingType.phone) phoneQuestionnaire.copy() else homeQuestionnaire.copy() val structureMap = if (tracingType == TracingType.phone) phoneStructureMap else homeStructureMap @@ -177,4 +268,33 @@ object FormService : KoinComponent { // throw Exception("Jeff") client.bundleUpload(resources, 30) } + + private suspend fun handleParse( + questionnaire: Questionnaire, + structureMap: StructureMap, + patientData: PatientData, + updateResponse: QuestionnaireResponseUpdater.() -> Unit + ): List { + var questionnaireResponse = responseGenerator.generateQuestionerResponse(questionnaire, patientData) + + val responseUpdater = QuestionnaireResponseUpdater( + questionnaire, + questionnaireResponse, + patientData.toLaunchContextMap(), + xFhirQueryResolver + ) + updateResponse.invoke(responseUpdater) + questionnaireResponse = responseUpdater.getQuestionnaireResponse() + println(iParser.encodeResourceToString(questionnaireResponse)) + val bundle = responseGenerator.extractBundle(questionnaire, questionnaireResponse, structureMap) + val bundleResources = bundle.entry.mapNotNull { it.resource } + questionnaireResponse.contained = bundleResources + + return listOf(questionnaireResponse.createBundleComponent()) + PatchMaker.createPatchedRequest( + iParser, + patientData.getAllItemMap(), + bundleResources + ) + } + } \ No newline at end of file diff --git a/server/src/main/kotlin/org/dtree/fhir/server/services/injection/ServicesInjection.kt b/server/src/main/kotlin/org/dtree/fhir/server/services/injection/ServicesInjection.kt index f75def3..dca560b 100644 --- a/server/src/main/kotlin/org/dtree/fhir/server/services/injection/ServicesInjection.kt +++ b/server/src/main/kotlin/org/dtree/fhir/server/services/injection/ServicesInjection.kt @@ -4,11 +4,13 @@ import org.dtree.fhir.server.services.form.FormService import org.dtree.fhir.server.services.patient.PatientService import org.dtree.fhir.server.services.stats.StatsService import org.dtree.fhir.server.services.tracing.TracingService +import org.dtree.fhir.server.services.util.UtilService import org.koin.dsl.module object ServicesInjection { val koinBeans = module { single { FormService } + single { UtilService } single { StatsService } single { TracingService } single { PatientService } diff --git a/server/src/main/kotlin/org/dtree/fhir/server/services/util/UtilService.kt b/server/src/main/kotlin/org/dtree/fhir/server/services/util/UtilService.kt new file mode 100644 index 0000000..764b779 --- /dev/null +++ b/server/src/main/kotlin/org/dtree/fhir/server/services/util/UtilService.kt @@ -0,0 +1,38 @@ +package org.dtree.fhir.server.services.util + +import ca.uhn.fhir.parser.IParser +import io.github.cdimascio.dotenv.Dotenv +import org.dtree.fhir.core.uploader.general.FailedToUploadException +import org.dtree.fhir.core.uploader.general.FhirClient +import org.dtree.fhir.core.utils.readFile +import org.hl7.fhir.r4.model.Bundle +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path +import kotlin.io.path.walk + +object UtilService : KoinComponent { + private val dotenv by inject() + private val iParser by inject() + private val fhirClient by inject() + + @OptIn(ExperimentalPathApi::class) + suspend fun runCli() { + val baseDir = dotenv["REPORT_DIR"] ?: return + val allFiles = Path(baseDir).walk() + allFiles.forEach { path -> + try { + val bundleRaw = path.toString().readFile() + val bundle = iParser.parseResource(Bundle::class.java, bundleRaw) + fhirClient.bundleUpload(bundle.entry, 2) + } catch (e: Exception) { + if (e !is FailedToUploadException) { + println("Something else happened, I am dying") + throw e + } + path.toFile().delete() + } + } + } +} \ No newline at end of file