mirror of
https://github.com/pcvolkmer/etl-processor.git
synced 2025-07-17 12:52:54 +00:00
fix: added missing genomeDe consent version and date; moved embedding consent resources into base class of GicsConsentService
This commit is contained in:
@ -0,0 +1,91 @@
|
|||||||
|
package dev.dnpm.etl.processor.consent;
|
||||||
|
|
||||||
|
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
|
||||||
|
import dev.pcvolkmer.mv64e.mtb.ConsentProvision;
|
||||||
|
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose;
|
||||||
|
import dev.pcvolkmer.mv64e.mtb.Mtb;
|
||||||
|
import dev.pcvolkmer.mv64e.mtb.Provision;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Consent;
|
||||||
|
import org.hl7.fhir.r4.model.Consent.ProvisionComponent;
|
||||||
|
import org.hl7.fhir.r4.model.Resource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public abstract class BaseConsentService implements ICheckConsent {
|
||||||
|
|
||||||
|
protected final GIcsConfigProperties gIcsConfigProperties;
|
||||||
|
protected Logger logger = LoggerFactory.getLogger(BaseConsentService.class);
|
||||||
|
|
||||||
|
public BaseConsentService(GIcsConfigProperties gIcsConfigProperties) {
|
||||||
|
this.gIcsConfigProperties = gIcsConfigProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void embedBroadConsentResources(Mtb mtbFile, Bundle broadConsent) {
|
||||||
|
for (Bundle.BundleEntryComponent entry : broadConsent.getEntry()) {
|
||||||
|
Resource resource = entry.getResource();
|
||||||
|
if (resource instanceof Consent) {
|
||||||
|
Map<String, Object> consentMap = new HashMap<>();
|
||||||
|
consentMap.put(resource.getIdElement().getIdPart(), resource);
|
||||||
|
mtbFile.getMetadata().getResearchConsents().add(consentMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGenomeDbProvisions(Mtb mtbFile, Bundle consentGnomeDe) {
|
||||||
|
for (Bundle.BundleEntryComponent entry : consentGnomeDe.getEntry()) {
|
||||||
|
Resource resource = entry.getResource();
|
||||||
|
if (!(resource instanceof Consent consentFhirResource)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect only one provision in collection, therefore get first or none
|
||||||
|
List<ProvisionComponent> provisions = consentFhirResource.getProvision().getProvision();
|
||||||
|
if (provisions.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var provisionComponent = provisions.getFirst();
|
||||||
|
|
||||||
|
String provisionCode = null;
|
||||||
|
if (provisionComponent.getCode() != null && !provisionComponent.getCode().isEmpty()) {
|
||||||
|
CodeableConcept codeableConcept = provisionComponent.getCode().getFirst();
|
||||||
|
if (codeableConcept.getCoding() != null && !codeableConcept.getCoding().isEmpty()) {
|
||||||
|
provisionCode = codeableConcept.getCoding().getFirst().getCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provisionCode != null) {
|
||||||
|
try {
|
||||||
|
ModelProjectConsentPurpose modelProjectConsentPurpose =
|
||||||
|
ModelProjectConsentPurpose.forValue(provisionCode);
|
||||||
|
|
||||||
|
if (ModelProjectConsentPurpose.SEQUENCING.equals(modelProjectConsentPurpose)) {
|
||||||
|
// CONVENTION: wrapping date is date of SEQUENCING consent
|
||||||
|
mtbFile.getMetadata().getModelProjectConsent().setDate(consentFhirResource.getDateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
Provision provision = Provision.builder()
|
||||||
|
.type(ConsentProvision.valueOf(provisionComponent.getType().name()))
|
||||||
|
.date(provisionComponent.getPeriod().getStart())
|
||||||
|
.purpose(modelProjectConsentPurpose)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
mtbFile.getMetadata().getModelProjectConsent().getProvisions().add(provision);
|
||||||
|
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
logger.error("Provision code '" + provisionCode + "' is unknown and cannot be mapped.", ioe.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mtbFile.getMetadata().getModelProjectConsent().getProvisions().isEmpty()) {
|
||||||
|
mtbFile.getMetadata().getModelProjectConsent().setVersion(gIcsConfigProperties.getGenomeDeConsentVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,14 @@
|
|||||||
package dev.dnpm.etl.processor.consent;
|
package dev.dnpm.etl.processor.consent;
|
||||||
|
|
||||||
|
import dev.pcvolkmer.mv64e.mtb.Mtb;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class ConsentCheckFileBased implements ICheckConsent{
|
public class ConsentCheckFileBased implements ICheckConsent {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ConsentCheckFileBased.class);
|
private static final Logger log = LoggerFactory.getLogger(ConsentCheckFileBased.class);
|
||||||
|
|
||||||
@ -40,4 +42,14 @@ public class ConsentCheckFileBased implements ICheckConsent{
|
|||||||
Date requestDate, ConsentDomain consentDomain) {
|
Date requestDate, ConsentDomain consentDomain) {
|
||||||
return ConsentProvisionType.NULL;
|
return ConsentProvisionType.NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void embedBroadConsentResources(Mtb mtbFile, Bundle broadConsent) {
|
||||||
|
throw new NotImplementedException("not intended to be implemented here!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addGenomeDbProvisions(Mtb mtbFile, Bundle consentGnomeDe) {
|
||||||
|
throw new NotImplementedException("not intended to be implemented here!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,10 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
|
||||||
public class GicsConsentService implements ICheckConsent {
|
public class GicsConsentService extends BaseConsentService {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(GicsConsentService.class);
|
private final Logger log = LoggerFactory.getLogger(GicsConsentService.class);
|
||||||
|
|
||||||
private final GIcsConfigProperties gIcsConfigProperties;
|
|
||||||
|
|
||||||
public static final String IS_CONSENTED_ENDPOINT = "/$isConsented";
|
public static final String IS_CONSENTED_ENDPOINT = "/$isConsented";
|
||||||
public static final String IS_POLICY_STATES_FOR_PERSON_ENDPOINT = "/$currentPolicyStatesForPerson";
|
public static final String IS_POLICY_STATES_FOR_PERSON_ENDPOINT = "/$currentPolicyStatesForPerson";
|
||||||
private final RetryTemplate retryTemplate;
|
private final RetryTemplate retryTemplate;
|
||||||
@ -54,7 +52,8 @@ public class GicsConsentService implements ICheckConsent {
|
|||||||
|
|
||||||
public GicsConsentService(GIcsConfigProperties gIcsConfigProperties,
|
public GicsConsentService(GIcsConfigProperties gIcsConfigProperties,
|
||||||
RetryTemplate retryTemplate, RestTemplate restTemplate, AppFhirConfig appFhirConfig) {
|
RetryTemplate retryTemplate, RestTemplate restTemplate, AppFhirConfig appFhirConfig) {
|
||||||
this.gIcsConfigProperties = gIcsConfigProperties;
|
super(gIcsConfigProperties);
|
||||||
|
|
||||||
this.retryTemplate = retryTemplate;
|
this.retryTemplate = retryTemplate;
|
||||||
this.restTemplate = restTemplate;
|
this.restTemplate = restTemplate;
|
||||||
this.fhirContext = appFhirConfig.fhirContext();
|
this.fhirContext = appFhirConfig.fhirContext();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package dev.dnpm.etl.processor.consent;
|
package dev.dnpm.etl.processor.consent;
|
||||||
|
|
||||||
|
import dev.pcvolkmer.mv64e.mtb.Mtb;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
||||||
@ -36,8 +36,8 @@ public interface ICheckConsent {
|
|||||||
* @return consent policies as bundle; <p>if empty patient has not been asked, yet.</p>
|
* @return consent policies as bundle; <p>if empty patient has not been asked, yet.</p>
|
||||||
*/
|
*/
|
||||||
default Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
|
default Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
|
||||||
return currentConsentForPersonAndTemplate(personIdentifierValue, ConsentDomain.Modelvorhaben64e,
|
return currentConsentForPersonAndTemplate(personIdentifierValue,
|
||||||
requestDate);
|
ConsentDomain.Modelvorhaben64e, requestDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,12 +50,17 @@ public interface ICheckConsent {
|
|||||||
*/
|
*/
|
||||||
Bundle currentConsentForPersonAndTemplate(String personIdentifierValue,
|
Bundle currentConsentForPersonAndTemplate(String personIdentifierValue,
|
||||||
ConsentDomain targetConsentDomain, Date requestDate);
|
ConsentDomain targetConsentDomain, Date requestDate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param consentBundle consent resource
|
* @param consentBundle consent resource
|
||||||
* @param requestDate date which must be within validation period of provision
|
* @param requestDate date which must be within validation period of provision
|
||||||
* @return type of provision, will be {@link ConsentProvisionType#NULL} if none is found.
|
* @return type of provision, will be {@link ConsentProvisionType#NULL} if none is found.
|
||||||
*/
|
*/
|
||||||
ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle,
|
ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle, Date requestDate,
|
||||||
Date requestDate, ConsentDomain consentDomain);
|
ConsentDomain consentDomain);
|
||||||
|
|
||||||
|
|
||||||
|
void embedBroadConsentResources(Mtb mtbFile, Bundle broadConsent);
|
||||||
|
|
||||||
|
void addGenomeDbProvisions(Mtb mtbFile, Bundle consentGnomeDe);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,13 @@ data class GIcsConfigProperties(
|
|||||||
/**
|
/**
|
||||||
* Consent Policy which should be used for consent check
|
* Consent Policy which should be used for consent check
|
||||||
*/
|
*/
|
||||||
val genomeDePolicySystem: String = "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV"
|
val genomeDePolicySystem: String = "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consent version (fixed version)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
val genomeDeConsentVersion: String = "2.0"
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "app.consent.gics"
|
const val NAME = "app.consent.gics"
|
||||||
|
@ -34,21 +34,16 @@ 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.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.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.r4.model.Bundle
|
|
||||||
import org.hl7.fhir.r4.model.Consent
|
import org.hl7.fhir.r4.model.Consent
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.context.ApplicationEventPublisher
|
import org.springframework.context.ApplicationEventPublisher
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import java.io.IOException
|
|
||||||
import java.lang.RuntimeException
|
import java.lang.RuntimeException
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@ -96,7 +91,7 @@ class RequestProcessor(
|
|||||||
* @return true if consent is given
|
* @return true if consent is given
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun consentGatedCheck(mtbFile: Mtb): Boolean {
|
fun consentGatedCheckAndTryEmbedding(mtbFile: Mtb): Boolean {
|
||||||
if (consentService == null) {
|
if (consentService == null) {
|
||||||
// consent check seems to be disabled
|
// consent check seems to be disabled
|
||||||
return true
|
return true
|
||||||
@ -127,8 +122,8 @@ class RequestProcessor(
|
|||||||
personIdentifierValue, requestDate
|
personIdentifierValue, requestDate
|
||||||
)
|
)
|
||||||
|
|
||||||
addGenomeDbProvisions(mtbFile, genomeDeConsent)
|
consentService.addGenomeDbProvisions(mtbFile, genomeDeConsent)
|
||||||
embedBroadConsentResources(mtbFile, broadConsent)
|
consentService.embedBroadConsentResources(mtbFile, broadConsent)
|
||||||
|
|
||||||
val broadConsentStatus = consentService.getProvisionTypeByPolicyCode(
|
val broadConsentStatus = consentService.getProvisionTypeByPolicyCode(
|
||||||
broadConsent,
|
broadConsent,
|
||||||
@ -157,20 +152,25 @@ class RequestProcessor(
|
|||||||
if (mtbFile.metadata == null) {
|
if (mtbFile.metadata == null) {
|
||||||
val mvhMetadata = MvhMetadata.builder().build()
|
val mvhMetadata = MvhMetadata.builder().build()
|
||||||
mtbFile.metadata = mvhMetadata
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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))
|
||||||
|
|
||||||
if (consentGatedCheck(mtbFile)) {
|
if (consentGatedCheckAndTryEmbedding(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))
|
||||||
@ -186,49 +186,6 @@ class RequestProcessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun embedBroadConsentResources(
|
|
||||||
mtbFile: Mtb, broadConsent: Bundle
|
|
||||||
) {
|
|
||||||
broadConsent.entry.forEach { it ->
|
|
||||||
mtbFile.metadata.researchConsents.add(mapOf(it.resource.id to it.resource as Consent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addGenomeDbProvisions(
|
|
||||||
mtbFile: Mtb, consentGnomeDe: Bundle
|
|
||||||
) {
|
|
||||||
consentGnomeDe.entry.forEach { it ->
|
|
||||||
{
|
|
||||||
val consentFhirResource = it.resource as Consent
|
|
||||||
|
|
||||||
// we expect only one provision in collection, therefore get first or none
|
|
||||||
val provisionComponent = consentFhirResource.provision.provision.firstOrNull()
|
|
||||||
val provisionCode =
|
|
||||||
provisionComponent?.code?.firstOrNull()?.coding?.firstOrNull()?.code
|
|
||||||
|
|
||||||
if (provisionCode != null) {
|
|
||||||
try {
|
|
||||||
val modelProjectConsentPurpose: ModelProjectConsentPurpose =
|
|
||||||
ModelProjectConsentPurpose.valueOf(provisionCode)
|
|
||||||
mtbFile.metadata.modelProjectConsent.provisions.add(
|
|
||||||
Provision.builder().type(
|
|
||||||
ConsentProvision.forValue(provisionComponent.type.name)
|
|
||||||
).date(provisionComponent.period.start).purpose(
|
|
||||||
modelProjectConsentPurpose
|
|
||||||
).build()
|
|
||||||
)
|
|
||||||
} catch (ioe: IOException) {
|
|
||||||
logger.error(
|
|
||||||
"provision code '$provisionCode' is unknown and cannot be mapped.",
|
|
||||||
ioe.toString()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> saveAndSend(request: MtbFileRequest<T>, pid: PatientId) {
|
private fun <T> saveAndSend(request: MtbFileRequest<T>, pid: PatientId) {
|
||||||
requestService.save(
|
requestService.save(
|
||||||
Request(
|
Request(
|
||||||
|
Reference in New Issue
Block a user