mirror of
https://github.com/pcvolkmer/mv64e-etl-processor
synced 2025-09-13 09:02:50 +00:00
132 fix consent check (#133)
This commit is contained in:
@@ -92,6 +92,7 @@ class AppConfiguration {
|
|||||||
restTemplate: RestTemplate,
|
restTemplate: RestTemplate,
|
||||||
appFhirConfig: AppFhirConfig
|
appFhirConfig: AppFhirConfig
|
||||||
): Generator {
|
): Generator {
|
||||||
|
logger.info("Selected 'GpasPseudonym Generator'")
|
||||||
return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate, appFhirConfig)
|
return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate, appFhirConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +103,7 @@ class AppConfiguration {
|
|||||||
)
|
)
|
||||||
@Bean
|
@Bean
|
||||||
fun buildinPseudonymGenerator(): Generator {
|
fun buildinPseudonymGenerator(): Generator {
|
||||||
|
logger.info("Selected 'BUILDIN Pseudonym Generator'")
|
||||||
return AnonymizingGenerator()
|
return AnonymizingGenerator()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -222,60 +222,56 @@ class ConsentProcessor(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param consentBundle consent resource
|
* @param consentBundle consent resource
|
||||||
* @param policyAndProvisionCode policyRule and provision code value
|
* @param targetCode policyRule and provision code value
|
||||||
* @param policyAndProvisionSystem policyRule and provision system value
|
* @param targetSystem policyRule and provision system value
|
||||||
* @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 [org.hl7.fhir.r4.model.Consent.ConsentProvisionType.NULL] if none is found.
|
* @return type of provision, will be [org.hl7.fhir.r4.model.Consent.ConsentProvisionType.NULL] if none is found.
|
||||||
*/
|
*/
|
||||||
fun getProvisionTypeByPolicyCode(
|
fun getProvisionTypeByPolicyCode(
|
||||||
consentBundle: Bundle,
|
consentBundle: Bundle, targetCode: String?, targetSystem: String?, requestDate: Date?
|
||||||
policyAndProvisionCode: String?,
|
|
||||||
policyAndProvisionSystem: String?,
|
|
||||||
requestDate: Date?
|
|
||||||
): Consent.ConsentProvisionType {
|
): Consent.ConsentProvisionType {
|
||||||
val entriesOfInterest = consentBundle.entry.filter { entry ->
|
val entriesOfInterest = consentBundle.entry.filter { entry ->
|
||||||
entry.resource.isResource && entry.resource.resourceType == ResourceType.Consent && (entry.resource as Consent).status == ConsentState.ACTIVE && checkCoding(
|
val isConsentResource =
|
||||||
policyAndProvisionCode,
|
entry.resource.isResource && entry.resource.resourceType == ResourceType.Consent
|
||||||
policyAndProvisionSystem,
|
val consentIsActive = (entry.resource as Consent).status == ConsentState.ACTIVE
|
||||||
(entry.resource as Consent).policyRule.codingFirstRep
|
|
||||||
) && isIsRequestDateInRange(
|
|
||||||
requestDate, (entry.resource as Consent).provision.period
|
|
||||||
)
|
|
||||||
}.map { consentWithTargetPolicy: BundleEntryComponent ->
|
|
||||||
val provision = (consentWithTargetPolicy.getResource() as Consent).getProvision()
|
|
||||||
val provisionComponentByCode =
|
|
||||||
provision.getProvision().stream().filter { prov: ProvisionComponent? ->
|
|
||||||
checkCoding(
|
|
||||||
policyAndProvisionCode,
|
|
||||||
policyAndProvisionSystem,
|
|
||||||
prov!!.getCodeFirstRep().getCodingFirstRep()
|
|
||||||
) && isIsRequestDateInRange(
|
|
||||||
requestDate, prov.getPeriod()
|
|
||||||
)
|
|
||||||
}.findFirst()
|
|
||||||
if (provisionComponentByCode.isPresent) {
|
|
||||||
// actual provision we search for
|
|
||||||
return@map provisionComponentByCode.get().getType()
|
|
||||||
} else {
|
|
||||||
if (provision.type != null) return provision.type
|
|
||||||
|
|
||||||
|
isConsentResource && consentIsActive && checkCoding(
|
||||||
|
targetCode, targetSystem, (entry.resource as Consent).policyRule.coding
|
||||||
|
) && isRequestDateInRange(requestDate, (entry.resource as Consent).provision.period)
|
||||||
|
}.map { entry: BundleEntryComponent ->
|
||||||
|
val consent = (entry.getResource() as Consent)
|
||||||
|
consent.provision.provision.filter { subProvision ->
|
||||||
|
isRequestDateInRange(requestDate, subProvision.period)
|
||||||
|
// search coding entries of current provision for code and system
|
||||||
|
subProvision.code.map { c -> c.coding }.flatten().firstOrNull { code ->
|
||||||
|
targetCode.equals(code.code) && targetSystem.equals(code.system)
|
||||||
|
} != null
|
||||||
|
}.map { subProvision ->
|
||||||
|
subProvision
|
||||||
}
|
}
|
||||||
return Consent.ConsentProvisionType.NULL
|
}.flatten()
|
||||||
}.firstOrNull()
|
|
||||||
|
|
||||||
if (entriesOfInterest == null) return Consent.ConsentProvisionType.NULL
|
if (!entriesOfInterest.isEmpty()) {
|
||||||
return entriesOfInterest
|
return entriesOfInterest.first().type
|
||||||
|
}
|
||||||
|
return Consent.ConsentProvisionType.NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkCoding(
|
fun checkCoding(
|
||||||
researchAllowedPolicyOid: String?, researchAllowedPolicySystem: String?, coding: Coding
|
researchAllowedPolicyOid: String?,
|
||||||
|
researchAllowedPolicySystem: String?,
|
||||||
|
policyRules: Collection<Coding>
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return coding.getSystem() == researchAllowedPolicySystem && (coding.getCode() == researchAllowedPolicyOid)
|
return policyRules.find { code ->
|
||||||
|
researchAllowedPolicySystem.equals(code.getSystem()) && (researchAllowedPolicyOid.equals(
|
||||||
|
code.getCode()
|
||||||
|
))
|
||||||
|
} != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isIsRequestDateInRange(requestdate: Date?, provPeriod: Period): Boolean {
|
fun isRequestDateInRange(requestDate: Date?, provPeriod: Period): Boolean {
|
||||||
val isRequestDateAfterOrEqualStart = provPeriod.getStart().compareTo(requestdate)
|
val isRequestDateAfterOrEqualStart = provPeriod.getStart().compareTo(requestDate)
|
||||||
val isRequestDateBeforeOrEqualEnd = provPeriod.getEnd().compareTo(requestdate)
|
val isRequestDateBeforeOrEqualEnd = provPeriod.getEnd().compareTo(requestDate)
|
||||||
return isRequestDateAfterOrEqualStart <= 0 && isRequestDateBeforeOrEqualEnd >= 0
|
return isRequestDateAfterOrEqualStart <= 0 && isRequestDateBeforeOrEqualEnd >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -78,7 +78,7 @@ class ConsentProcessorTest {
|
|||||||
val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb)
|
val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb)
|
||||||
|
|
||||||
assertThat(checkResult).isTrue
|
assertThat(checkResult).isTrue
|
||||||
assertThat(inputMtb.metadata.researchConsents).hasSize(13)
|
assertThat(inputMtb.metadata.researchConsents).hasSize(26)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -93,13 +93,13 @@ class ConsentProcessorTest {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
consent.provision.period.start =
|
consent.provision.period.start =
|
||||||
Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
|
Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
|
||||||
consent.provision.period.end =
|
consent.provision.period.end =
|
||||||
Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
||||||
|
|
||||||
val addProvision1 = consent.provision.addProvision()
|
val addProvision1 = consent.provision.addProvision()
|
||||||
addProvision1.setType(Consent.ConsentProvisionType.fromCode("permit"))
|
addProvision1.setType(Consent.ConsentProvisionType.fromCode("permit"))
|
||||||
addProvision1.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
|
addProvision1.period.start = Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
|
||||||
addProvision1.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
addProvision1.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
||||||
addProvision1.code.addLast(
|
addProvision1.code.addLast(
|
||||||
CodeableConcept(
|
CodeableConcept(
|
||||||
@@ -113,7 +113,7 @@ class ConsentProcessorTest {
|
|||||||
|
|
||||||
val addProvision2 = consent.provision.addProvision()
|
val addProvision2 = consent.provision.addProvision()
|
||||||
addProvision2.setType(Consent.ConsentProvisionType.fromCode("deny"))
|
addProvision2.setType(Consent.ConsentProvisionType.fromCode("deny"))
|
||||||
addProvision2.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
|
addProvision2.period.start = Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
|
||||||
addProvision2.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
addProvision2.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
||||||
addProvision2.code.addLast(
|
addProvision2.code.addLast(
|
||||||
CodeableConcept(
|
CodeableConcept(
|
||||||
@@ -130,13 +130,14 @@ class ConsentProcessorTest {
|
|||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@CsvSource(
|
@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-08-15T00: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,2025-08-15T00: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,2055-08-15T00: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,2021-08-15T00: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,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2060-08-15T00: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-08-15T00:00:00+02:00,DENY,provision is denied",
|
||||||
"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"
|
"unknownCode,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,NULL,code does not exist - therefore expect NULL",
|
||||||
|
"2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-08-15T00:00:00+02:00,NULL,system not found - therefore expect NULL",
|
||||||
)
|
)
|
||||||
fun getProvisionTypeByPolicyCode(
|
fun getProvisionTypeByPolicyCode(
|
||||||
code: String?, system: String?, timeStamp: String, expected: String?,
|
code: String?, system: String?, timeStamp: String, expected: String?,
|
||||||
@@ -155,6 +156,27 @@ class ConsentProcessorTest {
|
|||||||
.isEqualTo(Consent.ConsentProvisionType.valueOf(expected!!))
|
.isEqualTo(Consent.ConsentProvisionType.valueOf(expected!!))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getProvisionTypeOnEmptyConsent(
|
||||||
|
) {
|
||||||
|
val emptyResources = Bundle().addEntry(Bundle.BundleEntryComponent().setResource(Consent()))
|
||||||
|
|
||||||
|
val requestDate = Date.from(OffsetDateTime.parse("2025-08-15T00:00:00+02:00").toInstant())
|
||||||
|
|
||||||
|
val result: Consent.ConsentProvisionType =
|
||||||
|
consentProcessor.getProvisionTypeByPolicyCode(
|
||||||
|
emptyResources,
|
||||||
|
"anyCode",
|
||||||
|
"anySystem",
|
||||||
|
requestDate
|
||||||
|
)
|
||||||
|
assertThat(result).isNotNull()
|
||||||
|
|
||||||
|
|
||||||
|
assertThat(result).`as`("empty consent resource - expect NULL")
|
||||||
|
.isEqualTo(Consent.ConsentProvisionType.NULL)
|
||||||
|
}
|
||||||
|
|
||||||
fun getDummyBroadConsentBundle(): Bundle {
|
fun getDummyBroadConsentBundle(): Bundle {
|
||||||
val bundle: InputStream?
|
val bundle: InputStream?
|
||||||
try {
|
try {
|
||||||
|
@@ -97,18 +97,18 @@ class TransformationServiceTest {
|
|||||||
|
|
||||||
assertThat(mvhMetadata).isNotNull
|
assertThat(mvhMetadata).isNotNull
|
||||||
mvhMetadata.modelProjectConsent =
|
mvhMetadata.modelProjectConsent =
|
||||||
ModelProjectConsent.builder().date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z")))
|
ModelProjectConsent.builder().date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z")))
|
||||||
.version("1").provisions(
|
.version("1").provisions(
|
||||||
listOf(
|
listOf(
|
||||||
Provision.builder().type(ConsentProvision.PERMIT)
|
Provision.builder().type(ConsentProvision.PERMIT)
|
||||||
.purpose(ModelProjectConsentPurpose.SEQUENCING)
|
.purpose(ModelProjectConsentPurpose.SEQUENCING)
|
||||||
.date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build(),
|
.date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))).build(),
|
||||||
Provision.builder().type(ConsentProvision.PERMIT)
|
Provision.builder().type(ConsentProvision.PERMIT)
|
||||||
.purpose(ModelProjectConsentPurpose.REIDENTIFICATION)
|
.purpose(ModelProjectConsentPurpose.REIDENTIFICATION)
|
||||||
.date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build(),
|
.date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))).build(),
|
||||||
Provision.builder().type(ConsentProvision.DENY)
|
Provision.builder().type(ConsentProvision.DENY)
|
||||||
.purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION)
|
.purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION)
|
||||||
.date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build()
|
.date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))).build()
|
||||||
)
|
)
|
||||||
).build()
|
).build()
|
||||||
val consent = ConsentProcessorTest.getDummyGenomDeConsent()
|
val consent = ConsentProcessorTest.getDummyGenomDeConsent()
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user