1
0
mirror of https://github.com/pcvolkmer/etl-processor.git synced 2025-07-16 20:32:54 +00:00

fix: serialize fhir consent resources to string, object map

This commit is contained in:
Jakub Lidke
2025-07-15 17:05:26 +02:00
parent be592b1a2a
commit 600ac84ff3
6 changed files with 50 additions and 21 deletions

View File

@ -1,5 +1,9 @@
package dev.dnpm.etl.processor.consent;
import ca.uhn.fhir.context.FhirContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
import dev.pcvolkmer.mv64e.mtb.ConsentProvision;
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose;
@ -8,7 +12,6 @@ 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;
@ -20,24 +23,38 @@ 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) {
private final ObjectMapper objectMapper;
protected Logger logger = LoggerFactory.getLogger(BaseConsentService.class);
static FhirContext fhirCtx = FhirContext.forR4();
public BaseConsentService(GIcsConfigProperties gIcsConfigProperties,
ObjectMapper objectMapper) {
this.gIcsConfigProperties = gIcsConfigProperties;
this.objectMapper = objectMapper;
}
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);
// since jackson convertValue does not work here,
// we need another step to back to string, before we convert to object map
var asJsonString = fhirCtx.newJsonParser().encodeResourceToString(resource);
try {
var mapOfJson = objectMapper.readValue(asJsonString,
new TypeReference<HashMap<String, Object>>() {
});
mtbFile.getMetadata().getResearchConsents().add(mapOfJson);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
}
public void addGenomeDbProvisions(Mtb mtbFile, Bundle consentGnomeDe) {
public void addGenomeDbProvisions(Mtb mtbFile, Bundle consentGnomeDe) {
for (Bundle.BundleEntryComponent entry : consentGnomeDe.getEntry()) {
Resource resource = entry.getResource();
if (!(resource instanceof Consent consentFhirResource)) {
@ -67,7 +84,8 @@ public abstract class BaseConsentService implements ICheckConsent {
if (ModelProjectConsentPurpose.SEQUENCING.equals(modelProjectConsentPurpose)) {
// CONVENTION: wrapping date is date of SEQUENCING consent
mtbFile.getMetadata().getModelProjectConsent().setDate(consentFhirResource.getDateTime());
mtbFile.getMetadata().getModelProjectConsent()
.setDate(consentFhirResource.getDateTime());
}
Provision provision = Provision.builder()
@ -79,12 +97,15 @@ public abstract class BaseConsentService implements ICheckConsent {
mtbFile.getMetadata().getModelProjectConsent().getProvisions().add(provision);
} catch (IOException ioe) {
logger.error("Provision code '" + provisionCode + "' is unknown and cannot be mapped.", ioe.toString());
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());
mtbFile.getMetadata().getModelProjectConsent()
.setVersion(gIcsConfigProperties.getGenomeDeConsentVersion());
}
}
}

View File

@ -2,6 +2,7 @@ package dev.dnpm.etl.processor.consent;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.dnpm.etl.processor.config.AppFhirConfig;
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
import java.util.Date;
@ -51,8 +52,8 @@ public class GicsConsentService extends BaseConsentService {
private String url;
public GicsConsentService(GIcsConfigProperties gIcsConfigProperties,
RetryTemplate retryTemplate, RestTemplate restTemplate, AppFhirConfig appFhirConfig) {
super(gIcsConfigProperties);
RetryTemplate retryTemplate, RestTemplate restTemplate, AppFhirConfig appFhirConfig, ObjectMapper objectMapper) {
super(gIcsConfigProperties,objectMapper);
this.retryTemplate = retryTemplate;
this.restTemplate = restTemplate;

View File

@ -184,12 +184,13 @@ class AppConfiguration {
@Bean
@ConditionalOnProperty(name = ["app.consent.gics.enabled"], havingValue = "true")
fun gicsConsentService( gIcsConfigProperties: GIcsConfigProperties,
retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig): ICheckConsent {
retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig, getObjectMapper: ObjectMapper): ICheckConsent {
return GicsConsentService(
gIcsConfigProperties,
retryTemplate,
restTemplate,
appFhirConfig
appFhirConfig,
getObjectMapper
)
}

View File

@ -25,7 +25,6 @@ import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
import dev.pcvolkmer.mv64e.mtb.Mtb
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
import org.apache.commons.codec.digest.DigestUtils
import org.hl7.fhir.r4.model.Consent
/** Replaces patient ID with generated patient pseudonym
*
@ -295,11 +294,12 @@ infix fun Mtb.pseudonymizeWith(pseudonymizeService: PseudonymizeService) {
this.metadata?.researchConsents?.forEach { it ->
val entry = it ?: return@forEach
val key = entry.keys.first()
val consent = entry[key] as? Consent ?: return@forEach
val patRef= "Patient/$patientPseudonym"
consent.patient?.setReference(patRef)
consent.patient?.display = null
if (entry.contains("patient")) {
// here we expect only a patient reference any other data like display
// need to be removed, since may contain unsecure data
entry.remove("patient")
entry["patient"] = mapOf("reference" to "Patient/$patientPseudonym")
}
}
}

View File

@ -36,6 +36,7 @@ import dev.dnpm.etl.processor.pseudonym.anonymizeContentWith
import dev.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized
import dev.dnpm.etl.processor.pseudonym.pseudonymizeWith
import dev.pcvolkmer.mv64e.mtb.Mtb
import dev.pcvolkmer.mv64e.mtb.MvhSubmissionType
import org.apache.commons.codec.binary.Base32
import org.apache.commons.codec.digest.DigestUtils
import org.hl7.fhir.r4.model.Consent
@ -122,6 +123,10 @@ class RequestProcessor(
)
consentService.addGenomeDbProvisions(mtbFile, genomeDeConsent)
// fixme: currently we do not have information about submission type
if (!genomeDeConsent.entry.isEmpty()) mtbFile.metadata.type = MvhSubmissionType.INITIAL
consentService.embedBroadConsentResources(mtbFile, broadConsent)
val broadConsentStatus = consentService.getProvisionTypeByPolicyCode(

View File

@ -251,7 +251,8 @@ class ExtensionsTest {
private fun addConsentData(mtbFile: Mtb) {
val gIcsConfigProperties = GIcsConfigProperties("", "", "", true)
val baseConsentService = object : BaseConsentService(gIcsConfigProperties) {
val baseConsentService = object : BaseConsentService(gIcsConfigProperties,
JacksonConfig().objectMapper()) {
override fun getTtpBroadConsentStatus(personIdentifierValue: String?): TtpConsentStatus? {
throw NotImplementedError("dummy")
}