1
0
mirror of https://github.com/pcvolkmer/etl-processor.git synced 2025-07-01 14:12:55 +00:00

feat: Broad Consent and GenomDE Consent can be embedded into mtb file

This commit is contained in:
Jakub Lidke
2025-06-25 16:16:35 +02:00
parent 92f34459c3
commit 9ad4466d69
4 changed files with 103 additions and 12 deletions

View File

@ -214,13 +214,11 @@ public class GicsConsentService implements ICheckConsent {
} }
public Bundle getBroadConsent(String personIdentifierValue, Date requestDate) { public Bundle getBroadConsent(String personIdentifierValue, Date requestDate) {
String consentDomainName = gIcsConfigProperties.getBroadConsentDomainName();
return currentConsentForPersonAndTemplate(personIdentifierValue, ConsentDomain.BroadConsent, return currentConsentForPersonAndTemplate(personIdentifierValue, ConsentDomain.BroadConsent,
requestDate); requestDate);
} }
public Bundle getGnomDeConsent(String personIdentifierValue, Date requestDate) { public Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
return currentConsentForPersonAndTemplate(personIdentifierValue, return currentConsentForPersonAndTemplate(personIdentifierValue,
ConsentDomain.Modelvorhaben64e, requestDate); ConsentDomain.Modelvorhaben64e, requestDate);
} }

View File

@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.MtbFile import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.* import dev.dnpm.etl.processor.*
import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.config.AppConfigProperties
import dev.dnpm.etl.processor.consent.GicsConsentService
import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.monitoring.Report import dev.dnpm.etl.processor.monitoring.Report
import dev.dnpm.etl.processor.monitoring.Request import dev.dnpm.etl.processor.monitoring.Request
@ -32,9 +33,17 @@ 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.pseudonymizeWith import dev.dnpm.etl.processor.pseudonym.pseudonymizeWith
import dev.pcvolkmer.mv64e.mtb.ConsentProvision
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose
import dev.pcvolkmer.mv64e.mtb.Mtb import dev.pcvolkmer.mv64e.mtb.Mtb
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
import dev.pcvolkmer.mv64e.mtb.Provision
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.instance.model.api.IBaseResource
import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.Consent
import org.springframework.context.ApplicationEventPublisher import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.Instant import java.time.Instant
@ -48,7 +57,8 @@ class RequestProcessor(
private val requestService: RequestService, private val requestService: RequestService,
private val objectMapper: ObjectMapper, private val objectMapper: ObjectMapper,
private val applicationEventPublisher: ApplicationEventPublisher, private val applicationEventPublisher: ApplicationEventPublisher,
private val appConfigProperties: AppConfigProperties private val appConfigProperties: AppConfigProperties,
private val gicsConsentService: GicsConsentService?
) { ) {
fun processMtbFile(mtbFile: MtbFile) { fun processMtbFile(mtbFile: MtbFile) {
@ -69,12 +79,84 @@ class RequestProcessor(
fun processMtbFile(mtbFile: Mtb, requestId: RequestId) { fun processMtbFile(mtbFile: Mtb, requestId: RequestId) {
val pid = PatientId(mtbFile.patient.id) val pid = PatientId(mtbFile.patient.id)
addConsentToMtb(mtbFile)
mtbFile pseudonymizeWith pseudonymizeService mtbFile pseudonymizeWith pseudonymizeService
mtbFile anonymizeContentWith pseudonymizeService mtbFile anonymizeContentWith pseudonymizeService
val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile)) val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile))
saveAndSend(request, pid) saveAndSend(request, pid)
} }
fun addConsentToMtb(mtbFile: Mtb) {
if (gicsConsentService == null) return
// 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()
}
}
// fixme Date should be extracted from mtbFile
val consentGnomeDe =
gicsConsentService.getGenomDeConsent(mtbFile.patient.id, Date.from(Instant.now()))
addGenomeDbProvisions(mtbFile, consentGnomeDe)
// fixme Date should be extracted from mtbFile
val broadConsent =
gicsConsentService.getBroadConsent(mtbFile.patient.id, Date.from(Instant.now()))
embedBroadConsentResources(mtbFile, broadConsent)
}
fun embedBroadConsentResources(
mtbFile: Mtb,
broadConsent: Bundle
) {
broadConsent.entry.forEach { it ->
mtbFile.metadata.researchConsents.add(mapOf(it.resource.id to it as IBaseResource))
}
}
fun addGenomeDbProvisions(
mtbFile: Mtb,
consentGnomeDe: Bundle
) {
consentGnomeDe.entry.forEach { it ->
{
val consent = it.resource as Consent
val provisionComponent = consent.provision.provision.firstOrNull()
val provisionCode =
provisionComponent?.code?.firstOrNull()?.coding?.firstOrNull()?.code
var isValidCode = true
if (provisionCode != null) {
var modelProjectConsentPurpose: ModelProjectConsentPurpose =
ModelProjectConsentPurpose.SEQUENCING
if (provisionCode == "Teilnahme") {
modelProjectConsentPurpose = ModelProjectConsentPurpose.SEQUENCING
} else if (provisionCode == "Fallidentifizierung") {
modelProjectConsentPurpose = ModelProjectConsentPurpose.CASE_IDENTIFICATION
} else if (provisionCode == "Rekontaktierung") {
modelProjectConsentPurpose = ModelProjectConsentPurpose.REIDENTIFICATION
} else {
isValidCode = false
}
if (isValidCode) mtbFile.metadata.modelProjectConsent.provisions.add(
Provision.builder().type(
ConsentProvision.forValue(provisionComponent.type.name)
).date(provisionComponent.period.start).purpose(
modelProjectConsentPurpose
).build()
)
}
}
}
}
private fun <T> saveAndSend(request: MtbFileRequest<T>, pid: PatientId) { private fun <T> saveAndSend(request: MtbFileRequest<T>, pid: PatientId) {
requestService.save( requestService.save(
Request( Request(
@ -126,7 +208,9 @@ class RequestProcessor(
return null != lastMtbFileRequestForPatient return null != lastMtbFileRequestForPatient
&& !isLastRequestDeletion && !isLastRequestDeletion
&& lastMtbFileRequestForPatient.fingerprint == fingerprint(pseudonymizedMtbFileRequest) && lastMtbFileRequestForPatient.fingerprint == fingerprint(
pseudonymizedMtbFileRequest
)
} }
fun processDeletion(patientId: PatientId, isConsented: TtpConsentStatus) { fun processDeletion(patientId: PatientId, isConsented: TtpConsentStatus) {

View File

@ -22,6 +22,7 @@ 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.JacksonConfig
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.junit.jupiter.api.Nested import org.junit.jupiter.api.Nested
@ -39,6 +40,9 @@ import java.util.*
@ExtendWith(MockitoExtension::class) @ExtendWith(MockitoExtension::class)
class ExtensionsTest { class ExtensionsTest {
fun getObjectMapper() : ObjectMapper {
return JacksonConfig().objectMapper()
}
@Nested @Nested
inner class UsingBwhcDatamodel { inner class UsingBwhcDatamodel {
@ -46,13 +50,14 @@ class ExtensionsTest {
val FAKE_MTB_FILE_PATH = "fake_MTBFile.json" val FAKE_MTB_FILE_PATH = "fake_MTBFile.json"
val CLEAN_PATIENT_ID = "5dad2f0b-49c6-47d8-a952-7b9e9e0f7549" val CLEAN_PATIENT_ID = "5dad2f0b-49c6-47d8-a952-7b9e9e0f7549"
private fun fakeMtbFile(): MtbFile { private fun fakeMtbFile(): MtbFile {
val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream
return ObjectMapper().readValue(mtbFile, MtbFile::class.java) return getObjectMapper().readValue(mtbFile, MtbFile::class.java)
} }
private fun MtbFile.serialized(): String { private fun MtbFile.serialized(): String {
return ObjectMapper().writeValueAsString(this) return getObjectMapper().writeValueAsString(this)
} }
@Test @Test
@ -211,11 +216,11 @@ class ExtensionsTest {
private fun fakeMtbFile(): Mtb { private fun fakeMtbFile(): Mtb {
val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream
return ObjectMapper().readValue(mtbFile, Mtb::class.java) return getObjectMapper().readValue(mtbFile, Mtb::class.java)
} }
private fun Mtb.serialized(): String { private fun Mtb.serialized(): String {
return ObjectMapper().writeValueAsString(this) return getObjectMapper().writeValueAsString(this)
} }
@Test @Test

View File

@ -25,6 +25,7 @@ import dev.dnpm.etl.processor.Fingerprint
import dev.dnpm.etl.processor.PatientId import dev.dnpm.etl.processor.PatientId
import dev.dnpm.etl.processor.PatientPseudonym import dev.dnpm.etl.processor.PatientPseudonym
import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.config.AppConfigProperties
import dev.dnpm.etl.processor.consent.GicsConsentService
import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.monitoring.Request import dev.dnpm.etl.processor.monitoring.Request
import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.monitoring.RequestStatus
@ -59,7 +60,7 @@ class RequestProcessorTest {
private lateinit var requestService: RequestService private lateinit var requestService: RequestService
private lateinit var applicationEventPublisher: ApplicationEventPublisher private lateinit var applicationEventPublisher: ApplicationEventPublisher
private lateinit var appConfigProperties: AppConfigProperties private lateinit var appConfigProperties: AppConfigProperties
private lateinit var gicsConsentService : GicsConsentService
private lateinit var requestProcessor: RequestProcessor private lateinit var requestProcessor: RequestProcessor
@BeforeEach @BeforeEach
@ -68,7 +69,8 @@ class RequestProcessorTest {
@Mock transformationService: TransformationService, @Mock transformationService: TransformationService,
@Mock sender: RestMtbFileSender, @Mock sender: RestMtbFileSender,
@Mock requestService: RequestService, @Mock requestService: RequestService,
@Mock applicationEventPublisher: ApplicationEventPublisher @Mock applicationEventPublisher: ApplicationEventPublisher,
@Mock gicsConsentService: GicsConsentService
) { ) {
this.pseudonymizeService = pseudonymizeService this.pseudonymizeService = pseudonymizeService
this.transformationService = transformationService this.transformationService = transformationService
@ -76,6 +78,7 @@ class RequestProcessorTest {
this.requestService = requestService this.requestService = requestService
this.applicationEventPublisher = applicationEventPublisher this.applicationEventPublisher = applicationEventPublisher
this.appConfigProperties = AppConfigProperties(null) this.appConfigProperties = AppConfigProperties(null)
this.gicsConsentService = gicsConsentService
val objectMapper = ObjectMapper() val objectMapper = ObjectMapper()
@ -86,7 +89,8 @@ class RequestProcessorTest {
requestService, requestService,
objectMapper, objectMapper,
applicationEventPublisher, applicationEventPublisher,
appConfigProperties appConfigProperties,
gicsConsentService
) )
} }