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) {
String consentDomainName = gIcsConfigProperties.getBroadConsentDomainName();
return currentConsentForPersonAndTemplate(personIdentifierValue, ConsentDomain.BroadConsent,
requestDate);
}
public Bundle getGnomDeConsent(String personIdentifierValue, Date requestDate) {
public Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
return currentConsentForPersonAndTemplate(personIdentifierValue,
ConsentDomain.Modelvorhaben64e, requestDate);
}

View File

@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.*
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.monitoring.Report
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.anonymizeContentWith
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.MvhMetadata
import dev.pcvolkmer.mv64e.mtb.Provision
import org.apache.commons.codec.binary.Base32
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.stereotype.Service
import java.time.Instant
@ -48,7 +57,8 @@ class RequestProcessor(
private val requestService: RequestService,
private val objectMapper: ObjectMapper,
private val applicationEventPublisher: ApplicationEventPublisher,
private val appConfigProperties: AppConfigProperties
private val appConfigProperties: AppConfigProperties,
private val gicsConsentService: GicsConsentService?
) {
fun processMtbFile(mtbFile: MtbFile) {
@ -69,12 +79,84 @@ class RequestProcessor(
fun processMtbFile(mtbFile: Mtb, requestId: RequestId) {
val pid = PatientId(mtbFile.patient.id)
addConsentToMtb(mtbFile)
mtbFile pseudonymizeWith pseudonymizeService
mtbFile anonymizeContentWith pseudonymizeService
val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile))
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) {
requestService.save(
Request(
@ -126,7 +208,9 @@ class RequestProcessor(
return null != lastMtbFileRequestForPatient
&& !isLastRequestDeletion
&& lastMtbFileRequestForPatient.fingerprint == fingerprint(pseudonymizedMtbFileRequest)
&& lastMtbFileRequestForPatient.fingerprint == fingerprint(
pseudonymizedMtbFileRequest
)
}
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 de.ukw.ccc.bwhc.dto.*
import de.ukw.ccc.bwhc.dto.Patient
import dev.dnpm.etl.processor.config.JacksonConfig
import dev.pcvolkmer.mv64e.mtb.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Nested
@ -39,6 +40,9 @@ import java.util.*
@ExtendWith(MockitoExtension::class)
class ExtensionsTest {
fun getObjectMapper() : ObjectMapper {
return JacksonConfig().objectMapper()
}
@Nested
inner class UsingBwhcDatamodel {
@ -46,13 +50,14 @@ class ExtensionsTest {
val FAKE_MTB_FILE_PATH = "fake_MTBFile.json"
val CLEAN_PATIENT_ID = "5dad2f0b-49c6-47d8-a952-7b9e9e0f7549"
private fun fakeMtbFile(): MtbFile {
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 {
return ObjectMapper().writeValueAsString(this)
return getObjectMapper().writeValueAsString(this)
}
@Test
@ -211,11 +216,11 @@ class ExtensionsTest {
private fun fakeMtbFile(): Mtb {
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 {
return ObjectMapper().writeValueAsString(this)
return getObjectMapper().writeValueAsString(this)
}
@Test

View File

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