mirror of
https://github.com/pcvolkmer/etl-processor.git
synced 2025-07-17 12:52:54 +00:00
fix: reimplement broad consent and genomDE consent check and embedding them into mtb data
This commit is contained in:
@ -1,5 +1,8 @@
|
||||
package dev.dnpm.etl.processor.consent;
|
||||
|
||||
import java.util.Date;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -12,7 +15,29 @@ public class ConsentCheckFileBased implements ICheckConsent{
|
||||
}
|
||||
|
||||
@Override
|
||||
public TtpConsentStatus getTtpConsentStatus(String personIdentifierValue) {
|
||||
public TtpConsentStatus getTtpBroadConsentStatus(String personIdentifierValue) {
|
||||
return TtpConsentStatus.UNKNOWN_CHECK_FILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getBroadConsent(String personIdentifierValue, Date requestDate) {
|
||||
return ICheckConsent.super.getBroadConsent(personIdentifierValue, requestDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
|
||||
return ICheckConsent.super.getGenomDeConsent(personIdentifierValue, requestDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle currentConsentForPersonAndTemplate(String personIdentifierValue,
|
||||
ConsentDomain targetConsentDomain, Date requestDate) {
|
||||
return new Bundle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle,
|
||||
Date requestDate, ConsentDomain consentDomain) {
|
||||
return ConsentProvisionType.NULL;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,13 @@
|
||||
package dev.dnpm.etl.processor.consent;
|
||||
|
||||
public enum ConsentDomain {
|
||||
/**
|
||||
* MII Broad consent
|
||||
*/
|
||||
BroadConsent,
|
||||
|
||||
/**
|
||||
* GenomDe Modelvohaben §64e
|
||||
*/
|
||||
Modelvorhaben64e
|
||||
}
|
||||
|
@ -5,16 +5,24 @@ import ca.uhn.fhir.parser.DataFormatException;
|
||||
import dev.dnpm.etl.processor.config.AppFhirConfig;
|
||||
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Consent;
|
||||
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
||||
import org.hl7.fhir.r4.model.Consent.ConsentState;
|
||||
import org.hl7.fhir.r4.model.Consent.ProvisionComponent;
|
||||
import org.hl7.fhir.r4.model.DateType;
|
||||
import org.hl7.fhir.r4.model.Identifier;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
||||
import org.hl7.fhir.r4.model.Period;
|
||||
import org.hl7.fhir.r4.model.ResourceType;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
@ -63,7 +71,7 @@ public class GicsConsentService implements ICheckConsent {
|
||||
throw new IllegalArgumentException(
|
||||
"gICS base URL is empty - should call gICS with false configuration.");
|
||||
}
|
||||
url = UriComponentsBuilder.fromUriString(gIcsBaseUri).path(IS_CONSENTED_ENDPOINT)
|
||||
url = UriComponentsBuilder.fromUriString(gIcsBaseUri).path(endpoint)
|
||||
.toUriString();
|
||||
}
|
||||
return url;
|
||||
@ -91,8 +99,8 @@ public class GicsConsentService implements ICheckConsent {
|
||||
result.addParameter(new ParametersParameterComponent().setName("domain")
|
||||
.setValue(new StringType().setValue(configProperties.getBroadConsentDomainName())));
|
||||
result.addParameter(new ParametersParameterComponent().setName("policy").setValue(
|
||||
new Coding().setCode(configProperties.getPolicyCode())
|
||||
.setSystem(configProperties.getPolicySystem())));
|
||||
new Coding().setCode(configProperties.getBroadConsentPolicyCode())
|
||||
.setSystem(configProperties.getBroadConsentPolicySystem())));
|
||||
|
||||
/*
|
||||
* is mandatory parameter, but we ignore it via additional configuration parameter
|
||||
@ -152,7 +160,7 @@ public class GicsConsentService implements ICheckConsent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TtpConsentStatus getTtpConsentStatus(String personIdentifierValue) {
|
||||
public TtpConsentStatus getTtpBroadConsentStatus(String personIdentifierValue) {
|
||||
var parameter = GicsConsentService.getIsConsentedRequestParam(gIcsConfigProperties,
|
||||
personIdentifierValue);
|
||||
|
||||
@ -199,30 +207,14 @@ public class GicsConsentService implements ICheckConsent {
|
||||
private String getConsentDomain(ConsentDomain targetConsentDomain) {
|
||||
String consentDomain;
|
||||
switch (targetConsentDomain) {
|
||||
case BroadConsent -> {
|
||||
consentDomain = gIcsConfigProperties.getBroadConsentDomainName();
|
||||
}
|
||||
case Modelvorhaben64e -> {
|
||||
consentDomain = gIcsConfigProperties.getGnomDeConsentDomainName();
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException(
|
||||
case BroadConsent -> consentDomain = gIcsConfigProperties.getBroadConsentDomainName();
|
||||
case Modelvorhaben64e -> consentDomain = gIcsConfigProperties.getGenomDeConsentDomainName();
|
||||
default -> throw new IllegalArgumentException(
|
||||
"target ConsentDomain is missing but must be provided!");
|
||||
}
|
||||
}
|
||||
return consentDomain;
|
||||
}
|
||||
|
||||
public Bundle getBroadConsent(String personIdentifierValue, Date requestDate) {
|
||||
return currentConsentForPersonAndTemplate(personIdentifierValue, ConsentDomain.BroadConsent,
|
||||
requestDate);
|
||||
}
|
||||
|
||||
public Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
|
||||
return currentConsentForPersonAndTemplate(personIdentifierValue,
|
||||
ConsentDomain.Modelvorhaben64e, requestDate);
|
||||
}
|
||||
|
||||
protected static Parameters buildRequestParameterCurrentPolicyStatesForPerson(
|
||||
GIcsConfigProperties gIcsConfigProperties, String personIdentifierValue, Date requestDate,
|
||||
String targetDomain) {
|
||||
@ -264,9 +256,9 @@ public class GicsConsentService implements ICheckConsent {
|
||||
return TtpConsentStatus.FAILED_TO_ASK;
|
||||
}
|
||||
if (isConsented.booleanValue()) {
|
||||
return TtpConsentStatus.CONSENTED;
|
||||
return TtpConsentStatus.BROAD_CONSENT_GIVEN;
|
||||
} else {
|
||||
return TtpConsentStatus.CONSENT_MISSING_OR_REJECTED;
|
||||
return TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED;
|
||||
}
|
||||
} else if (response instanceof OperationOutcome outcome) {
|
||||
log.error("failed to get consent status from ttp. probably configuration error. "
|
||||
@ -278,4 +270,84 @@ public class GicsConsentService implements ICheckConsent {
|
||||
}
|
||||
return TtpConsentStatus.FAILED_TO_ASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param consentBundle consent resource
|
||||
* @param requestDate date which must be within validation period of provision
|
||||
* @return type of provision, will be {@link ConsentProvisionType#NULL} if none is found.
|
||||
*/
|
||||
public ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle,
|
||||
Date requestDate, ConsentDomain consentDomain) {
|
||||
String code;
|
||||
String system;
|
||||
if (ConsentDomain.BroadConsent == consentDomain) {
|
||||
code = gIcsConfigProperties.getBroadConsentPolicyCode();
|
||||
system = gIcsConfigProperties.getBroadConsentPolicySystem();
|
||||
} else if (ConsentDomain.Modelvorhaben64e == consentDomain) {
|
||||
code = gIcsConfigProperties.getGenomeDePolicyCode();
|
||||
system = gIcsConfigProperties.getGenomeDePolicySystem();
|
||||
} else {
|
||||
throw new NotImplementedException("unknown consent domain " + consentDomain.name());
|
||||
}
|
||||
|
||||
Optional<ConsentProvisionType> provisionTypeByPolicyCode = getProvisionTypeByPolicyCode(
|
||||
consentBundle, code,
|
||||
system, requestDate);
|
||||
return provisionTypeByPolicyCode.orElse(ConsentProvisionType.NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param consentBundle consent resource
|
||||
* @param policyAndProvisionCode policyRule and provision code value
|
||||
* @param policyAndProvisionSystem policyRule and provision system value
|
||||
* @param requestDate date which must be within validation period of provision
|
||||
* @return type of provision, will be {@link ConsentProvisionType#NULL} if none is found.
|
||||
*/
|
||||
public Optional<ConsentProvisionType> getProvisionTypeByPolicyCode(Bundle consentBundle,
|
||||
String policyAndProvisionCode, String policyAndProvisionSystem, Date requestDate) {
|
||||
return consentBundle.getEntry().stream().filter(entry -> {
|
||||
if (entry.getResource().getResourceType() != ResourceType.Consent) {
|
||||
// no consent in bundle
|
||||
return false;
|
||||
}
|
||||
|
||||
Consent consent = (Consent) entry.getResource();
|
||||
|
||||
// consent ist active and its policy rule must fits search criteria
|
||||
return consent.getStatus() == ConsentState.ACTIVE && checkCoding(
|
||||
policyAndProvisionCode, policyAndProvisionSystem,
|
||||
consent.getPolicyRule().getCodingFirstRep()) && isIsRequestDateInRange(requestDate,
|
||||
consent.getProvision().getPeriod());
|
||||
|
||||
}).map(consentWithTargetPolicy -> {
|
||||
ProvisionComponent provision = ((Consent) consentWithTargetPolicy.getResource()).getProvision();
|
||||
var provisionComponentByCode = provision.getProvision().stream().filter(prov ->
|
||||
|
||||
checkCoding(policyAndProvisionCode, policyAndProvisionSystem,
|
||||
prov.getCodeFirstRep().getCodingFirstRep()) && isIsRequestDateInRange(
|
||||
requestDate, prov.getPeriod())
|
||||
|
||||
).findFirst();
|
||||
|
||||
if (provisionComponentByCode.isPresent()) {
|
||||
// actual provision we search for
|
||||
return provisionComponentByCode.get().getType();
|
||||
}
|
||||
// no fitting nested provision found - fall back to wrapping provision with default value
|
||||
return provision.getType();
|
||||
}).findFirst().or(() -> Optional.of(ConsentProvisionType.NULL));
|
||||
}
|
||||
|
||||
protected static boolean checkCoding(String researchAllowedPolicyOid,
|
||||
String researchAllowedPolicySystem, Coding coding) {
|
||||
|
||||
return coding.getSystem().equals(researchAllowedPolicySystem) && coding.getCode()
|
||||
.equals(researchAllowedPolicyOid);
|
||||
}
|
||||
|
||||
protected static boolean isIsRequestDateInRange(Date requestdate, Period provPeriod) {
|
||||
var isRequestDateAfterOrEqualStart = provPeriod.getStart().compareTo(requestdate);
|
||||
var isRequestDateBeforeOrEqualEnd = provPeriod.getEnd().compareTo(requestdate);
|
||||
return isRequestDateAfterOrEqualStart <= 0 && isRequestDateBeforeOrEqualEnd >= 0;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,61 @@
|
||||
package dev.dnpm.etl.processor.consent;
|
||||
|
||||
|
||||
import java.util.Date;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
||||
|
||||
public interface ICheckConsent {
|
||||
|
||||
TtpConsentStatus getTtpConsentStatus(String personIdentifierValue);
|
||||
/**
|
||||
* Get broad consent status for a patient identifier
|
||||
*
|
||||
* @param personIdentifierValue patient identifier used for consent data
|
||||
* @return status of broad consent
|
||||
* @apiNote cannot not differ between not asked and rejected
|
||||
*/
|
||||
TtpConsentStatus getTtpBroadConsentStatus(String personIdentifierValue);
|
||||
|
||||
/**
|
||||
* Get broad consent policies with respect to a request date
|
||||
*
|
||||
* @param personIdentifierValue patient identifier used for consent data
|
||||
* @param requestDate target date until consent data should be considered
|
||||
* @return consent policies as bundle; <p>if empty patient has not been asked, yet.</p>
|
||||
*/
|
||||
default Bundle getBroadConsent(String personIdentifierValue, Date requestDate) {
|
||||
return currentConsentForPersonAndTemplate(personIdentifierValue, ConsentDomain.BroadConsent,
|
||||
requestDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 'GenomDe Modelvorhaben §64e' consent policies with respect to a request date
|
||||
*
|
||||
* @param personIdentifierValue patient identifier used for consent data
|
||||
* @param requestDate target date until consent data should be considered
|
||||
* @return consent policies as bundle; <p>if empty patient has not been asked, yet.</p>
|
||||
*/
|
||||
default Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
|
||||
return currentConsentForPersonAndTemplate(personIdentifierValue, ConsentDomain.BroadConsent,
|
||||
requestDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get consent policies with respect to a request date
|
||||
*
|
||||
* @param personIdentifierValue patient identifier used for consent data
|
||||
* @param targetConsentDomain domain which should be used to request consent
|
||||
* @param requestDate target date until consent data should be considered
|
||||
* @return consent policies as bundle; <p>if empty patient has not been asked, yet.</p>
|
||||
*/
|
||||
Bundle currentConsentForPersonAndTemplate(String personIdentifierValue,
|
||||
ConsentDomain targetConsentDomain, Date requestDate);
|
||||
/**
|
||||
*
|
||||
* @param consentBundle consent resource
|
||||
* @param requestDate date which must be within validation period of provision
|
||||
* @return type of provision, will be {@link ConsentProvisionType#NULL} if none is found.
|
||||
*/
|
||||
ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle,
|
||||
Date requestDate, ConsentDomain consentDomain);
|
||||
}
|
||||
|
@ -4,17 +4,35 @@ public enum TtpConsentStatus {
|
||||
/**
|
||||
* Valid consent found
|
||||
*/
|
||||
CONSENTED,
|
||||
|
||||
CONSENT_MISSING_OR_REJECTED,
|
||||
|
||||
BROAD_CONSENT_GIVEN,
|
||||
/**
|
||||
* Due technical problems consent status is unknown
|
||||
* Missing or rejected...actually unknown
|
||||
*/
|
||||
FAILED_TO_ASK,
|
||||
BROAD_CONSENT_MISSING_OR_REJECTED,
|
||||
/**
|
||||
* No Broad consent policy found
|
||||
*/
|
||||
BROAD_CONSENT_MISSING,
|
||||
/**
|
||||
* Research policy has been rejected
|
||||
*/
|
||||
BROAD_CONSENT_REJECTED,
|
||||
|
||||
GENOM_DE_CONSENT_SEQUENCING_PERMIT,
|
||||
/**
|
||||
* No GenomDE consent policy found
|
||||
*/
|
||||
GENOM_DE_CONSENT_MISSING,
|
||||
/**
|
||||
* GenomDE consent policy found, but has been rejected
|
||||
*/
|
||||
GENOM_DE_SEQUENCING_REJECTED,
|
||||
/**
|
||||
* Consent status is validate via file property 'consent.status'
|
||||
*/
|
||||
UNKNOWN_CHECK_FILE
|
||||
UNKNOWN_CHECK_FILE,
|
||||
/**
|
||||
* Due technical problems consent status is unknown
|
||||
*/
|
||||
FAILED_TO_ASK
|
||||
}
|
||||
|
@ -87,17 +87,27 @@ data class GIcsConfigProperties(
|
||||
/**
|
||||
* Domain of Modelvorhaben 64e consent resources
|
||||
**/
|
||||
val gnomDeConsentDomainName: String = "GenomDE_MV",
|
||||
val genomDeConsentDomainName: String = "GenomDE_MV",
|
||||
|
||||
/**
|
||||
* Value to expect in case of positiv consent
|
||||
*/
|
||||
val policyCode: String = "2.16.840.1.113883.3.1937.777.24.5.3.6",
|
||||
val broadConsentPolicyCode: String = "2.16.840.1.113883.3.1937.777.24.5.3.6",
|
||||
|
||||
/**
|
||||
* Consent Policy which should be used for consent check
|
||||
*/
|
||||
val policySystem: String = "urn:oid:2.16.840.1.113883.3.1937.777.24.5.3"
|
||||
val broadConsentPolicySystem: String = "urn:oid:2.16.840.1.113883.3.1937.777.24.5.3",
|
||||
|
||||
/**
|
||||
* Value to expect in case of positiv consent
|
||||
*/
|
||||
val genomeDePolicyCode: String = "sequencing",
|
||||
|
||||
/**
|
||||
* Consent Policy which should be used for consent check
|
||||
*/
|
||||
val genomeDePolicySystem: String = "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV"
|
||||
) {
|
||||
companion object {
|
||||
const val NAME = "app.consent.gics"
|
||||
|
@ -63,16 +63,16 @@ class MtbFileRestController(
|
||||
}
|
||||
|
||||
private fun checkConsentStatus(mtbFile: MtbFile): Pair<TtpConsentStatus, Boolean> {
|
||||
var ttpConsentStatus = iCheckConsent.getTtpConsentStatus(mtbFile.patient.id)
|
||||
var ttpConsentStatus = iCheckConsent.getTtpBroadConsentStatus(mtbFile.patient.id)
|
||||
|
||||
val isConsentOK =
|
||||
(ttpConsentStatus.equals(TtpConsentStatus.UNKNOWN_CHECK_FILE) && mtbFile.consent.status == Consent.Status.ACTIVE) ||
|
||||
ttpConsentStatus.equals(
|
||||
TtpConsentStatus.CONSENTED
|
||||
TtpConsentStatus.BROAD_CONSENT_GIVEN
|
||||
)
|
||||
if (ttpConsentStatus.equals(TtpConsentStatus.UNKNOWN_CHECK_FILE) && mtbFile.consent.status == Consent.Status.REJECTED) {
|
||||
// in case ttp check is disabled - we propagate rejected status anyway
|
||||
ttpConsentStatus = TtpConsentStatus.CONSENT_MISSING_OR_REJECTED
|
||||
ttpConsentStatus = TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED
|
||||
}
|
||||
return Pair(ttpConsentStatus, isConsentOK)
|
||||
}
|
||||
|
@ -23,7 +23,8 @@ 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.ConsentDomain
|
||||
import dev.dnpm.etl.processor.consent.ICheckConsent
|
||||
import dev.dnpm.etl.processor.consent.TtpConsentStatus
|
||||
import dev.dnpm.etl.processor.monitoring.Report
|
||||
import dev.dnpm.etl.processor.monitoring.Request
|
||||
@ -44,9 +45,15 @@ 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.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.context.ApplicationEventPublisher
|
||||
import org.springframework.stereotype.Service
|
||||
import java.io.IOException
|
||||
import java.lang.RuntimeException
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.util.*
|
||||
|
||||
@Service
|
||||
@ -58,9 +65,10 @@ class RequestProcessor(
|
||||
private val objectMapper: ObjectMapper,
|
||||
private val applicationEventPublisher: ApplicationEventPublisher,
|
||||
private val appConfigProperties: AppConfigProperties,
|
||||
private val gicsConsentService: GicsConsentService?
|
||||
private val consentService: ICheckConsent?
|
||||
) {
|
||||
|
||||
private var logger: Logger = LoggerFactory.getLogger("RequestProcessor")
|
||||
fun processMtbFile(mtbFile: MtbFile) {
|
||||
processMtbFile(mtbFile, randomRequestId())
|
||||
}
|
||||
@ -77,21 +85,79 @@ class RequestProcessor(
|
||||
processMtbFile(mtbFile, randomRequestId())
|
||||
}
|
||||
|
||||
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)
|
||||
/**
|
||||
* In case an instance of {@link ICheckConsent} is active, consent will be embedded and checked.
|
||||
*
|
||||
* Logik:
|
||||
* * <c>true</c> IF consent check is disabled.
|
||||
* * <c>true</c> IF broad consent (BC) has been given.
|
||||
* * <c>true</c> BC has been asked AND declined but genomDe consent has been consented.
|
||||
* * ELSE <c>false</c> is returned.
|
||||
*
|
||||
* @param mtbFile File v2 (will be enriched with consent data)
|
||||
* @return true if consent is given
|
||||
*
|
||||
*/
|
||||
fun consentGatedCheck(mtbFile: Mtb): Boolean {
|
||||
if (consentService == null) {
|
||||
// consent check seems to be disabled
|
||||
return true
|
||||
}
|
||||
|
||||
fun addConsentToMtb(mtbFile: Mtb) {
|
||||
if (gicsConsentService == null) return
|
||||
initMetaDataAtMtbFile(mtbFile)
|
||||
|
||||
val personIdentifierValue = extractPatientIdentifier(mtbFile)
|
||||
val requestDate = Date.from(Instant.now(Clock.system(ZoneId.of("ECT"))))
|
||||
|
||||
// 1. Broad consent Entry exists?
|
||||
// 1.1. -> yes and research consent is given -> send mtb file
|
||||
// 1.2. -> no -> return status error - consent has not been asked
|
||||
// 2. -> Broad consent found but rejected -> is GenomDe consent provision 'sequencing' given?
|
||||
// 2.1 -> yes -> send mtb file
|
||||
// 2.2 -> no -> warn/info no consent given
|
||||
|
||||
/*
|
||||
* broad consent
|
||||
*/
|
||||
val broadConsent = consentService.getBroadConsent(personIdentifierValue, requestDate)
|
||||
val broadConsentHasBeenAsked = !broadConsent.entry.isEmpty()
|
||||
|
||||
// fast exit - if patient has not been asked, we can skip and exit
|
||||
if (!broadConsentHasBeenAsked) return false
|
||||
|
||||
val genomeDeConsent = consentService.getGenomDeConsent(
|
||||
personIdentifierValue, requestDate
|
||||
)
|
||||
|
||||
addGenomeDbProvisions(mtbFile, genomeDeConsent)
|
||||
embedBroadConsentResources(mtbFile, broadConsent)
|
||||
|
||||
val broadConsentStatus = consentService.getProvisionTypeByPolicyCode(
|
||||
broadConsent,
|
||||
requestDate,
|
||||
ConsentDomain.BroadConsent
|
||||
)
|
||||
|
||||
val genomDeSequencingStatus = consentService.getProvisionTypeByPolicyCode(
|
||||
genomeDeConsent, requestDate,
|
||||
ConsentDomain.Modelvorhaben64e
|
||||
)
|
||||
|
||||
if (Consent.ConsentProvisionType.PERMIT == broadConsentStatus) return true
|
||||
if (Consent.ConsentProvisionType.DENY == broadConsentStatus && Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus) return true
|
||||
if (Consent.ConsentProvisionType.NULL == broadConsentStatus) {
|
||||
// bc not asked
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
private fun initMetaDataAtMtbFile(mtbFile: Mtb) {
|
||||
// init metadata if necessary
|
||||
if (mtbFile.metadata == null) {
|
||||
val mvhMetadata = MvhMetadata.builder().build();
|
||||
val mvhMetadata = MvhMetadata.builder().build()
|
||||
mtbFile.metadata = mvhMetadata
|
||||
if (mtbFile.metadata.researchConsents == null) {
|
||||
mtbFile.metadata.researchConsents = mutableListOf()
|
||||
@ -101,21 +167,29 @@ class RequestProcessor(
|
||||
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 processMtbFile(mtbFile: Mtb, requestId: RequestId) {
|
||||
val pid = PatientId(extractPatientIdentifier(mtbFile))
|
||||
|
||||
if (consentGatedCheck(mtbFile)) {
|
||||
mtbFile pseudonymizeWith pseudonymizeService
|
||||
mtbFile anonymizeContentWith pseudonymizeService
|
||||
val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile))
|
||||
saveAndSend(request, pid)
|
||||
} else {
|
||||
logger.warn("consent check failed file will not be processed further!")
|
||||
applicationEventPublisher.publishEvent(
|
||||
ResponseEvent(
|
||||
requestId, Instant.now(), RequestStatus.NO_CONSENT
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun embedBroadConsentResources(
|
||||
mtbFile: Mtb,
|
||||
broadConsent: Bundle
|
||||
mtbFile: Mtb, broadConsent: Bundle
|
||||
) {
|
||||
broadConsent.entry.forEach { it ->
|
||||
mtbFile.metadata.researchConsents.add(mapOf(it.resource.id to it as IBaseResource))
|
||||
@ -123,37 +197,37 @@ class RequestProcessor(
|
||||
}
|
||||
|
||||
fun addGenomeDbProvisions(
|
||||
mtbFile: Mtb,
|
||||
consentGnomeDe: Bundle
|
||||
mtbFile: Mtb, consentGnomeDe: Bundle
|
||||
) {
|
||||
consentGnomeDe.entry.forEach { it ->
|
||||
{
|
||||
val consent = it.resource as Consent
|
||||
val provisionComponent = consent.provision.provision.firstOrNull()
|
||||
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
|
||||
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(
|
||||
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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,9 +246,7 @@ class RequestProcessor(
|
||||
if (appConfigProperties.duplicationDetection && isDuplication(request)) {
|
||||
applicationEventPublisher.publishEvent(
|
||||
ResponseEvent(
|
||||
request.requestId,
|
||||
Instant.now(),
|
||||
RequestStatus.DUPLICATION
|
||||
request.requestId, Instant.now(), RequestStatus.DUPLICATION
|
||||
)
|
||||
)
|
||||
return
|
||||
@ -206,9 +278,7 @@ class RequestProcessor(
|
||||
val isLastRequestDeletion =
|
||||
requestService.isLastRequestWithKnownStatusDeletion(patientPseudonym)
|
||||
|
||||
return null != lastMtbFileRequestForPatient
|
||||
&& !isLastRequestDeletion
|
||||
&& lastMtbFileRequestForPatient.fingerprint == fingerprint(
|
||||
return null != lastMtbFileRequestForPatient && !isLastRequestDeletion && lastMtbFileRequestForPatient.fingerprint == fingerprint(
|
||||
pseudonymizedMtbFileRequest
|
||||
)
|
||||
}
|
||||
@ -222,9 +292,12 @@ class RequestProcessor(
|
||||
val patientPseudonym = pseudonymizeService.patientPseudonym(patientId)
|
||||
|
||||
val requestStatus: RequestStatus = when (isConsented) {
|
||||
TtpConsentStatus.CONSENT_MISSING_OR_REJECTED -> RequestStatus.NO_CONSENT
|
||||
TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, TtpConsentStatus.BROAD_CONSENT_MISSING, TtpConsentStatus.BROAD_CONSENT_REJECTED -> RequestStatus.NO_CONSENT
|
||||
TtpConsentStatus.FAILED_TO_ASK -> RequestStatus.ERROR
|
||||
TtpConsentStatus.CONSENTED, TtpConsentStatus.UNKNOWN_CHECK_FILE -> RequestStatus.UNKNOWN
|
||||
TtpConsentStatus.BROAD_CONSENT_GIVEN, TtpConsentStatus.UNKNOWN_CHECK_FILE -> RequestStatus.UNKNOWN
|
||||
TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, TtpConsentStatus.GENOM_DE_CONSENT_MISSING, TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED -> {
|
||||
throw RuntimeException("processDelete should never deal with '" + isConsented.name + "' consent status. This is a bug and need to be fixed!")
|
||||
}
|
||||
}
|
||||
|
||||
requestService.save(
|
||||
@ -242,10 +315,7 @@ class RequestProcessor(
|
||||
|
||||
applicationEventPublisher.publishEvent(
|
||||
ResponseEvent(
|
||||
requestId,
|
||||
Instant.now(),
|
||||
responseStatus.status,
|
||||
when (responseStatus.status) {
|
||||
requestId, Instant.now(), responseStatus.status, when (responseStatus.status) {
|
||||
RequestStatus.WARNING, RequestStatus.ERROR -> Optional.of(responseStatus.body)
|
||||
else -> Optional.empty()
|
||||
}
|
||||
@ -276,10 +346,10 @@ class RequestProcessor(
|
||||
|
||||
private fun fingerprint(s: String): Fingerprint {
|
||||
return Fingerprint(
|
||||
Base32().encodeAsString(DigestUtils.sha256(s))
|
||||
.replace("=", "")
|
||||
.lowercase()
|
||||
Base32().encodeAsString(DigestUtils.sha256(s)).replace("=", "").lowercase()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun extractPatientIdentifier(mtbFile: Mtb): String = mtbFile.patient.id
|
||||
|
@ -4,13 +4,19 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.dnpm.etl.processor.config.AppConfiguration;
|
||||
import dev.dnpm.etl.processor.config.AppFhirConfig;
|
||||
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Date;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
||||
import org.hl7.fhir.r4.model.Identifier;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
|
||||
@ -21,8 +27,11 @@ import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
@ -58,7 +67,7 @@ public class GicsConsentServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTtpConsentStatus() {
|
||||
void getTtpBroadConsentStatus() {
|
||||
final Parameters responseConsented = new Parameters().addParameter(
|
||||
new ParametersParameterComponent().setName("consented")
|
||||
.setValue(new BooleanType().setValue(true)));
|
||||
@ -70,8 +79,8 @@ public class GicsConsentServiceTest {
|
||||
.encodeResourceToString(responseConsented),
|
||||
MediaType.APPLICATION_JSON));
|
||||
|
||||
var consentStatus = gicsConsentService.getTtpConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.CONSENTED);
|
||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.BROAD_CONSENT_GIVEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -87,8 +96,8 @@ public class GicsConsentServiceTest {
|
||||
.encodeResourceToString(responseRevoced),
|
||||
MediaType.APPLICATION_JSON));
|
||||
|
||||
var consentStatus = gicsConsentService.getTtpConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED);
|
||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED);
|
||||
}
|
||||
|
||||
|
||||
@ -105,7 +114,7 @@ public class GicsConsentServiceTest {
|
||||
.encodeResourceToString(responseErrorOutcome),
|
||||
MediaType.APPLICATION_JSON));
|
||||
|
||||
var consentStatus = gicsConsentService.getTtpConsentStatus("123456");
|
||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK);
|
||||
}
|
||||
|
||||
@ -114,11 +123,50 @@ public class GicsConsentServiceTest {
|
||||
|
||||
String pid = "12345678";
|
||||
var result = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson(gIcsConfigProperties,
|
||||
pid, Date.from(Instant.now()),gIcsConfigProperties.getGnomDeConsentDomainName());
|
||||
pid, Date.from(Instant.now()),gIcsConfigProperties.getGenomDeConsentDomainName());
|
||||
|
||||
assertThat(result.getParameter().size()).as("should contain 3 parameter resources").isEqualTo(3);
|
||||
|
||||
assertThat(((StringType)result.getParameter("domain").getValue()).getValue()).isEqualTo(gIcsConfigProperties.getGnomDeConsentDomainName());
|
||||
assertThat(((StringType)result.getParameter("domain").getValue()).getValue()).isEqualTo(gIcsConfigProperties.getGenomDeConsentDomainName());
|
||||
assertThat(((Identifier)result.getParameter("personIdentifier").getValue()).getValue()).isEqualTo(pid);
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-07-23T00:00:00+02:00,PERMIT,expect permit",
|
||||
"2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-06-23T00:00:00+02:00,PERMIT,expect permit date is exactly on start",
|
||||
"2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2055-06-23T00:00:00+02:00,PERMIT,expect permit date is exactly on end",
|
||||
"2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2021-06-23T00:00:00+02:00,NULL,date is before start",
|
||||
"2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2060-06-23T00:00:00+02:00,NULL,date is after end",
|
||||
"2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-07-23T00:00:00+02:00,NULL,system not found - therefore expect NULL",
|
||||
"2.16.840.1.113883.3.1937.777.24.5.3.27,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-07-23T00:00:00+02:00,DENY,provision is denied"})
|
||||
void getProvisionTypeByPolicyCode(String code, String system, String timeStamp, String expected,
|
||||
String desc) {
|
||||
|
||||
var testData = getDummyBroadConsent();
|
||||
|
||||
Date requestDate = Date.from(OffsetDateTime.parse(timeStamp).toInstant());
|
||||
|
||||
var result = gicsConsentService.getProvisionTypeByPolicyCode(testData, code, system, requestDate);
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result).isNotEmpty();
|
||||
|
||||
assertThat(result.get()).as(desc).isEqualTo(ConsentProvisionType.valueOf(expected));
|
||||
}
|
||||
|
||||
private Bundle getDummyBroadConsent() {
|
||||
|
||||
InputStream bundle;
|
||||
try {
|
||||
bundle = new ClassPathResource(
|
||||
"fake_broadConsent_gics_response_permit.json").getInputStream();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return FhirContext.forR4().newJsonParser().parseResource(Bundle.class, bundle);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class MtbFileRestControllerTest {
|
||||
|
||||
verify(requestProcessor, times(1)).processDeletion(
|
||||
anyValueClass(),
|
||||
org.mockito.kotlin.eq(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED)
|
||||
org.mockito.kotlin.eq(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
|
||||
)
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@ class MtbFileRestControllerTest {
|
||||
@ValueSource(strings = ["ACTIVE", "REJECTED"])
|
||||
fun shouldProcessPostRequest(status: String) {
|
||||
|
||||
whenever(gicsConsentService.getTtpConsentStatus(any())).thenReturn(TtpConsentStatus.CONSENTED)
|
||||
whenever(gicsConsentService.getTtpBroadConsentStatus(any())).thenReturn(TtpConsentStatus.BROAD_CONSENT_GIVEN)
|
||||
|
||||
mockMvc.post("/mtbfile") {
|
||||
content =
|
||||
@ -169,7 +169,7 @@ class MtbFileRestControllerTest {
|
||||
@ValueSource(strings = ["ACTIVE", "REJECTED"])
|
||||
fun shouldProcessPostRequestWithRejectedConsent(status: String) {
|
||||
|
||||
whenever(gicsConsentService.getTtpConsentStatus(any())).thenReturn(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED)
|
||||
whenever(gicsConsentService.getTtpBroadConsentStatus(any())).thenReturn(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
|
||||
|
||||
mockMvc.post("/mtbfile") {
|
||||
content =
|
||||
@ -184,7 +184,7 @@ class MtbFileRestControllerTest {
|
||||
// consent status from ttp should override file consent value
|
||||
verify(requestProcessor, times(1)).processDeletion(
|
||||
anyValueClass(),
|
||||
org.mockito.kotlin.eq(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED)
|
||||
org.mockito.kotlin.eq(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
|
||||
)
|
||||
}
|
||||
|
||||
@ -201,7 +201,7 @@ class MtbFileRestControllerTest {
|
||||
anyValueClass(),
|
||||
org.mockito.kotlin.eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
|
||||
)
|
||||
verify(gicsConsentService, times(0)).getTtpConsentStatus(any())
|
||||
verify(gicsConsentService, times(0)).getTtpBroadConsentStatus(any())
|
||||
|
||||
}
|
||||
}
|
||||
@ -253,7 +253,7 @@ class MtbFileRestControllerTest {
|
||||
|
||||
verify(requestProcessor, times(1)).processDeletion(
|
||||
anyValueClass(), org.mockito.kotlin.eq(
|
||||
TtpConsentStatus.CONSENT_MISSING_OR_REJECTED
|
||||
TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -283,11 +283,12 @@ class MtbFileRestControllerTest {
|
||||
|
||||
@BeforeEach
|
||||
fun setup(
|
||||
@Mock requestProcessor: RequestProcessor
|
||||
@Mock requestProcessor: RequestProcessor,
|
||||
@Mock gicsConsentService: GicsConsentService
|
||||
) {
|
||||
this.requestProcessor = requestProcessor
|
||||
val controller = MtbFileRestController(requestProcessor,
|
||||
ConsentCheckFileBased()
|
||||
gicsConsentService
|
||||
)
|
||||
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
||||
}
|
||||
|
@ -24,12 +24,15 @@ import de.ukw.ccc.bwhc.dto.Diagnosis
|
||||
import de.ukw.ccc.bwhc.dto.Icd10
|
||||
import de.ukw.ccc.bwhc.dto.MtbFile
|
||||
import dev.dnpm.etl.processor.config.JacksonConfig
|
||||
import dev.pcvolkmer.mv64e.mtb.ConsentProvision
|
||||
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
|
||||
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import dev.pcvolkmer.mv64e.mtb.Mtb
|
||||
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
|
||||
import dev.pcvolkmer.mv64e.mtb.Provision
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource
|
||||
import org.hl7.fhir.r4.model.CodeableConcept
|
||||
import org.hl7.fhir.r4.model.Coding
|
||||
@ -124,21 +127,53 @@ class TransformationServiceTest {
|
||||
|
||||
@Test
|
||||
fun shouldTransformConsent() {
|
||||
val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build();
|
||||
val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build()
|
||||
|
||||
assertThat(mvhMetadata).isNotNull
|
||||
mvhMetadata.modelProjectConsent =
|
||||
ModelProjectConsent.builder().date(Date.from(Instant.now())).version("1").build()
|
||||
val consent1 = org.hl7.fhir.r4.model.Consent()
|
||||
consent1.id = "consent 1 id"
|
||||
consent1.patient.reference = "Patient/1234-pat1"
|
||||
ModelProjectConsent.builder().date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z")))
|
||||
.version("1").provisions(
|
||||
listOf(
|
||||
Provision.builder().type(ConsentProvision.PERMIT)
|
||||
.purpose(ModelProjectConsentPurpose.SEQUENCING)
|
||||
.date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build(),
|
||||
Provision.builder().type(ConsentProvision.PERMIT)
|
||||
.purpose(ModelProjectConsentPurpose.REIDENTIFICATION)
|
||||
.date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build(),
|
||||
Provision.builder().type(ConsentProvision.DENY)
|
||||
.purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION)
|
||||
.date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build()
|
||||
)
|
||||
).build()
|
||||
val consent = getDummyConsent()
|
||||
|
||||
consent1.provision.setType(org.hl7.fhir.r4.model.Consent.ConsentProvisionType.fromCode("deny"))
|
||||
consent1.provision.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
|
||||
consent1.provision.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
||||
mvhMetadata.researchConsents = mutableListOf()
|
||||
mvhMetadata.researchConsents.add(mapOf(consent.id to consent as IBaseResource))
|
||||
|
||||
val mtbFile = Mtb.builder().metadata(mvhMetadata).build()
|
||||
|
||||
val transformed = service.transform(mtbFile)
|
||||
assertThat(transformed.metadata.modelProjectConsent.date).isNotNull
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun getDummyConsent(): org.hl7.fhir.r4.model.Consent {
|
||||
val modelVorhabenConsent = org.hl7.fhir.r4.model.Consent()
|
||||
modelVorhabenConsent.id = "consent 1 id"
|
||||
modelVorhabenConsent.patient.reference = "Patient/1234-pat1"
|
||||
|
||||
modelVorhabenConsent.provision.setType(
|
||||
org.hl7.fhir.r4.model.Consent.ConsentProvisionType.fromCode(
|
||||
"deny"
|
||||
)
|
||||
)
|
||||
modelVorhabenConsent.provision.period.start =
|
||||
Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
|
||||
modelVorhabenConsent.provision.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
||||
|
||||
|
||||
val addProvision1 = consent1.provision.addProvision()
|
||||
val addProvision1 = modelVorhabenConsent.provision.addProvision()
|
||||
addProvision1.setType(org.hl7.fhir.r4.model.Consent.ConsentProvisionType.fromCode("permit"))
|
||||
addProvision1.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
|
||||
addProvision1.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
||||
@ -152,7 +187,7 @@ class TransformationServiceTest {
|
||||
)
|
||||
)
|
||||
|
||||
val addProvision2 = consent1.provision.addProvision()
|
||||
val addProvision2 = modelVorhabenConsent.provision.addProvision()
|
||||
addProvision2.setType(org.hl7.fhir.r4.model.Consent.ConsentProvisionType.fromCode("deny"))
|
||||
addProvision2.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
|
||||
addProvision2.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
||||
@ -165,14 +200,5 @@ class TransformationServiceTest {
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
mvhMetadata.researchConsents = mutableListOf()
|
||||
mvhMetadata.researchConsents.add(mapOf(consent1.id to consent1 as IBaseResource))
|
||||
|
||||
val mtbFile = Mtb.builder().metadata(mvhMetadata).build()
|
||||
|
||||
val transformed = service.transform(mtbFile)
|
||||
assertThat(transformed.metadata.modelProjectConsent.date).isNotNull
|
||||
|
||||
}
|
||||
return modelVorhabenConsent
|
||||
}
|
@ -606,7 +606,7 @@
|
||||
},
|
||||
"provision": [
|
||||
{
|
||||
"type": "permit",
|
||||
"type": "deny",
|
||||
"period": {
|
||||
"start": "2025-06-23T00:00:00+02:00",
|
||||
"end": "2055-06-23T00:00:00+02:00"
|
||||
|
Reference in New Issue
Block a user