1
0
mirror of https://github.com/pcvolkmer/etl-processor.git synced 2025-07-17 21:02:54 +00:00

fix: pseudonymize patient reference at embedded consent resources

This commit is contained in:
Jakub Lidke
2025-07-10 15:15:14 +02:00
parent cc525d3f4f
commit 90d1378e12
4 changed files with 127 additions and 82 deletions

View File

@ -21,7 +21,9 @@ package dev.dnpm.etl.processor.pseudonym
import de.ukw.ccc.bwhc.dto.MtbFile import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.PatientId import dev.dnpm.etl.processor.PatientId
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
import dev.pcvolkmer.mv64e.mtb.Mtb import dev.pcvolkmer.mv64e.mtb.Mtb
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
import org.apache.commons.codec.digest.DigestUtils import org.apache.commons.codec.digest.DigestUtils
import org.hl7.fhir.r4.model.Consent import org.hl7.fhir.r4.model.Consent
@ -291,12 +293,13 @@ infix fun Mtb.pseudonymizeWith(pseudonymizeService: PseudonymizeService) {
it.patient.id = patientPseudonym it.patient.id = patientPseudonym
} }
// FIXME: MUST CREATE TESTCASE - NEEDS TESTING!! this.metadata?.researchConsents?.forEach { it ->
this.metadata?.researchConsents?.forEach { it -> { val entry = it ?: return@forEach
val consent = it as? Consent val key = entry.keys.first()
consent?.patient?.reference = "Patient/$patientPseudonym" val consent = entry[key] as? Consent ?: return@forEach
consent?.patient?.display = null val patRef= "Patient/$patientPseudonym"
} consent.patient?.setReference(patRef)
consent.patient?.display = null
} }
} }
@ -326,3 +329,23 @@ infix fun Mtb.anonymizeContentWith(pseudonymizeService: PseudonymizeService) {
// TODO all other properties // TODO all other properties
} }
fun Mtb.ensureMetaDataIsInitialized() {
// init metadata if necessary
if (this.metadata == null) {
val mvhMetadata = MvhMetadata.builder().build()
this.metadata = mvhMetadata
}
if (this.metadata.researchConsents == null) {
this.metadata.researchConsents = mutableListOf()
}
if (this.metadata.modelProjectConsent == null) {
this.metadata.modelProjectConsent = ModelProjectConsent()
this.metadata.modelProjectConsent.provisions = mutableListOf()
} else
if (this.metadata.modelProjectConsent.provisions != null) {
// make sure list can be changed
this.metadata.modelProjectConsent.provisions =
this.metadata.modelProjectConsent.provisions.toMutableList()
}
}

View File

@ -33,10 +33,9 @@ import dev.dnpm.etl.processor.monitoring.RequestType
import dev.dnpm.etl.processor.output.* import dev.dnpm.etl.processor.output.*
import dev.dnpm.etl.processor.pseudonym.PseudonymizeService import dev.dnpm.etl.processor.pseudonym.PseudonymizeService
import dev.dnpm.etl.processor.pseudonym.anonymizeContentWith import dev.dnpm.etl.processor.pseudonym.anonymizeContentWith
import dev.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized
import dev.dnpm.etl.processor.pseudonym.pseudonymizeWith import dev.dnpm.etl.processor.pseudonym.pseudonymizeWith
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
import dev.pcvolkmer.mv64e.mtb.Mtb import dev.pcvolkmer.mv64e.mtb.Mtb
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
import org.apache.commons.codec.binary.Base32 import org.apache.commons.codec.binary.Base32
import org.apache.commons.codec.digest.DigestUtils import org.apache.commons.codec.digest.DigestUtils
import org.hl7.fhir.r4.model.Consent import org.hl7.fhir.r4.model.Consent
@ -97,7 +96,7 @@ class RequestProcessor(
return true return true
} }
initMetaDataAtMtbFile(mtbFile) mtbFile.ensureMetaDataIsInitialized()
val personIdentifierValue = extractPatientIdentifier(mtbFile) val personIdentifierValue = extractPatientIdentifier(mtbFile)
val requestDate = Date.from(Instant.now(Clock.systemUTC())) val requestDate = Date.from(Instant.now(Clock.systemUTC()))
@ -146,27 +145,6 @@ class RequestProcessor(
return false return false
} }
private fun initMetaDataAtMtbFile(mtbFile: Mtb) {
// init metadata if necessary
if (mtbFile.metadata == null) {
val mvhMetadata = MvhMetadata.builder().build()
mtbFile.metadata = mvhMetadata
}
if (mtbFile.metadata.researchConsents == null) {
mtbFile.metadata.researchConsents = mutableListOf()
}
if (mtbFile.metadata.modelProjectConsent == null) {
mtbFile.metadata.modelProjectConsent = ModelProjectConsent()
mtbFile.metadata.modelProjectConsent.provisions = mutableListOf()
} else
if (mtbFile.metadata.modelProjectConsent.provisions != null) {
// make sure list can be changed
mtbFile.metadata.modelProjectConsent.provisions =
mtbFile.metadata.modelProjectConsent.provisions.toMutableList()
}
}
fun processMtbFile(mtbFile: Mtb, requestId: RequestId) { fun processMtbFile(mtbFile: Mtb, requestId: RequestId) {
val pid = PatientId(extractPatientIdentifier(mtbFile)) val pid = PatientId(extractPatientIdentifier(mtbFile))

View File

@ -22,9 +22,15 @@ package dev.dnpm.etl.processor.pseudonym
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.* import de.ukw.ccc.bwhc.dto.*
import de.ukw.ccc.bwhc.dto.Patient import de.ukw.ccc.bwhc.dto.Patient
import dev.dnpm.etl.processor.config.GIcsConfigProperties
import dev.dnpm.etl.processor.config.JacksonConfig import dev.dnpm.etl.processor.config.JacksonConfig
import dev.dnpm.etl.processor.consent.BaseConsentService
import dev.dnpm.etl.processor.consent.ConsentDomain
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.services.TransformationServiceTest
import dev.pcvolkmer.mv64e.mtb.* import dev.pcvolkmer.mv64e.mtb.*
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.hl7.fhir.r4.model.Bundle
import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
@ -231,6 +237,8 @@ class ExtensionsTest {
}.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
val mtbFile = fakeMtbFile() val mtbFile = fakeMtbFile()
mtbFile.ensureMetaDataIsInitialized()
addConsentData(CLEAN_PATIENT_ID,mtbFile)
mtbFile.pseudonymizeWith(pseudonymizeService) mtbFile.pseudonymizeWith(pseudonymizeService)
@ -238,6 +246,39 @@ class ExtensionsTest {
assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID) assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID)
} }
private fun addConsentData(cleanPatientId: String, mtbFile: Mtb) {
val gIcsConfigProperties = GIcsConfigProperties("","","", true)
val baseConsentService = object: BaseConsentService(gIcsConfigProperties){
override fun getTtpBroadConsentStatus(personIdentifierValue: String?): TtpConsentStatus? {
throw NotImplementedError("dummy")
}
override fun currentConsentForPersonAndTemplate(
personIdentifierValue: String?,
targetConsentDomain: ConsentDomain?,
requestDate: Date?
): Bundle? {
throw NotImplementedError("dummy")
}
override fun getProvisionTypeByPolicyCode(
consentBundle: Bundle?,
requestDate: Date?,
consentDomain: ConsentDomain?
): org.hl7.fhir.r4.model.Consent.ConsentProvisionType? {
throw NotImplementedError("dummy")
}
}
val bundle = Bundle()
val dummyConsent = TransformationServiceTest.getDummyConsent()
dummyConsent.patient.reference = "Patient/$cleanPatientId"
bundle.addEntry().resource= dummyConsent
baseConsentService.embedBroadConsentResources(mtbFile,bundle)
}
@Test @Test
fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) { fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) {
doAnswer { doAnswer {

View File

@ -156,9 +156,9 @@ class TransformationServiceTest {
assertThat(transformed.metadata.modelProjectConsent.date).isNotNull assertThat(transformed.metadata.modelProjectConsent.date).isNotNull
} }
}
fun getDummyConsent(): org.hl7.fhir.r4.model.Consent { companion object {
fun getDummyConsent(): org.hl7.fhir.r4.model.Consent {
val modelVorhabenConsent = org.hl7.fhir.r4.model.Consent() val modelVorhabenConsent = org.hl7.fhir.r4.model.Consent()
modelVorhabenConsent.id = "consent 1 id" modelVorhabenConsent.id = "consent 1 id"
modelVorhabenConsent.patient.reference = "Patient/1234-pat1" modelVorhabenConsent.patient.reference = "Patient/1234-pat1"
@ -170,7 +170,8 @@ fun getDummyConsent(): org.hl7.fhir.r4.model.Consent {
) )
modelVorhabenConsent.provision.period.start = modelVorhabenConsent.provision.period.start =
Date.from(Instant.parse("2025-06-23T00:00:00.00Z")) Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
modelVorhabenConsent.provision.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z")) modelVorhabenConsent.provision.period.end =
Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
val addProvision1 = modelVorhabenConsent.provision.addProvision() val addProvision1 = modelVorhabenConsent.provision.addProvision()
@ -201,4 +202,6 @@ fun getDummyConsent(): org.hl7.fhir.r4.model.Consent {
) )
) )
return modelVorhabenConsent return modelVorhabenConsent
}
}
} }