Skip to content

Commit 882fed8

Browse files
committed
Change Consent Key Parsing To Enum
1 parent 6de804a commit 882fed8

19 files changed

+293
-7447
lines changed

src/main/java/de/medizininformatikinitiative/torch/consent/ConsentCodeMapper.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,26 @@
22

33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import de.medizininformatikinitiative.torch.model.mapping.ConsentKey;
56

67
import java.io.File;
78
import java.io.IOException;
8-
import java.util.*;
9+
import java.util.ArrayList;
10+
import java.util.Collections;
11+
import java.util.HashMap;
12+
import java.util.HashSet;
13+
import java.util.Iterator;
14+
import java.util.List;
15+
import java.util.Map;
16+
import java.util.Set;
917

1018

1119
/**
1220
* Provides a Map of all consent codes belonging to a consent key e.g. "yes-yes-yes-yes"
1321
*/
1422
public class ConsentCodeMapper {
1523

16-
private final Map<String, List<String>> consentMap;
24+
private final Map<ConsentKey, List<String>> consentMap;
1725
private final ObjectMapper objectMapper;
1826

1927

@@ -30,7 +38,7 @@ private void buildConsentMap(String filePath) throws IOException {
3038
File file = new File(filePath);
3139
JsonNode consentMappingData = objectMapper.readTree(file.getAbsoluteFile());
3240
for (JsonNode consent : consentMappingData) {
33-
String keyCode = consent.get("key").get("code").asText();
41+
ConsentKey keyCode = ConsentKey.fromString(consent.get("key").get("code").asText());
3442
List<String> relevantCodes = new ArrayList<>();
3543

3644
JsonNode fixedCriteria = consent.get("fixedCriteria");
@@ -48,7 +56,7 @@ private void buildConsentMap(String filePath) throws IOException {
4856
}
4957
}
5058

51-
public Set<String> getRelevantCodes(String key) {
59+
public Set<String> getRelevantCodes(ConsentKey key) {
5260
return new HashSet<>(consentMap.getOrDefault(key, Collections.emptyList()));
5361
}
54-
}
62+
}

src/main/java/de/medizininformatikinitiative/torch/consent/ConsentFetcher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import de.medizininformatikinitiative.torch.model.fhir.Query;
99
import de.medizininformatikinitiative.torch.model.management.PatientBatch;
1010
import de.medizininformatikinitiative.torch.model.management.PatientResourceBundle;
11+
import de.medizininformatikinitiative.torch.model.mapping.ConsentKey;
1112
import de.medizininformatikinitiative.torch.service.DataStore;
1213
import de.medizininformatikinitiative.torch.util.ResourceUtils;
1314
import org.hl7.fhir.r4.model.Consent;
@@ -98,7 +99,7 @@ private static Map<String, Provisions> mergeAllProvisions(Map<String, Collection
9899
* @param batch A list of patient IDs to process in this batch.
99100
* @return A {@link Flux} emitting maps containing consent information structured by patient ID and consent codes.
100101
*/
101-
public Mono<PatientBatchWithConsent> buildConsentInfo(String key, PatientBatch batch) {
102+
public Mono<PatientBatchWithConsent> buildConsentInfo(ConsentKey key, PatientBatch batch) {
102103
logger.debug("Starting to build consent info for key {} and {} patients", key, batch.ids().size());
103104

104105
Set<String> codes = mapper.getRelevantCodes(key);

src/main/java/de/medizininformatikinitiative/torch/consent/ConsentHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import de.medizininformatikinitiative.torch.model.consent.PatientBatchWithConsent;
55
import de.medizininformatikinitiative.torch.model.fhir.Query;
66
import de.medizininformatikinitiative.torch.model.management.PatientBatch;
7+
import de.medizininformatikinitiative.torch.model.mapping.ConsentKey;
78
import de.medizininformatikinitiative.torch.service.DataStore;
89
import de.medizininformatikinitiative.torch.util.ResourceUtils;
910
import org.hl7.fhir.r4.model.Encounter;
@@ -73,7 +74,7 @@ private static Mono<Map<String, Collection<Encounter>>> groupEncounterByPatient(
7374
* @param batch Batch of patient IDs.
7475
* @return {@link Mono<PatientBatchWithConsent>} containing all required provisions by patient with valid times.
7576
*/
76-
public Mono<PatientBatchWithConsent> fetchAndBuildConsentInfo(String consentKey, PatientBatch batch) {
77+
public Mono<PatientBatchWithConsent> fetchAndBuildConsentInfo(ConsentKey consentKey, PatientBatch batch) {
7778
return consentFetcher.buildConsentInfo(consentKey, batch)
7879
.flatMap(this::adjustConsentPeriodsByPatientEncounters);
7980
}

src/main/java/de/medizininformatikinitiative/torch/model/crtdl/Crtdl.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
44
import com.fasterxml.jackson.annotation.JsonProperty;
55
import com.fasterxml.jackson.databind.JsonNode;
6-
import org.slf4j.Logger;
7-
import org.slf4j.LoggerFactory;
6+
import de.medizininformatikinitiative.torch.model.mapping.ConsentKey;
87

98
import java.util.Optional;
109

@@ -17,26 +16,26 @@ public record Crtdl(
1716
@JsonProperty(required = true)
1817
DataExtraction dataExtraction
1918
) {
20-
private static final Logger logger = LoggerFactory.getLogger(Crtdl.class);
2119

2220
public Crtdl {
2321
requireNonNull(cohortDefinition);
2422
requireNonNull(dataExtraction);
2523
}
2624

27-
public Optional<String> consentKey() {
25+
public Optional<ConsentKey> consentKey() {
2826
JsonNode inclusionCriteria = cohortDefinition.get("inclusionCriteria");
2927
if (inclusionCriteria != null && inclusionCriteria.isArray()) {
3028
for (JsonNode criteriaGroup : inclusionCriteria) {
3129
for (JsonNode criteria : criteriaGroup) {
3230
JsonNode context = criteria.get("context");
3331
if (context != null && "Einwilligung".equals(context.get("code").asText())) {
34-
JsonNode termcodes = criteria.get("termCodes");
35-
if (termcodes != null && termcodes.isArray()) {
36-
JsonNode firstTermcode = termcodes.get(0);
37-
if (firstTermcode != null && firstTermcode.has("code")) {
38-
return Optional.of(firstTermcode.get("code").asText());
39-
}
32+
JsonNode firstTermcode = criteria.get("termCodes").get(0);
33+
if (firstTermcode != null && firstTermcode.has("code")) {
34+
String code = firstTermcode.get("code").asText();
35+
ConsentKey key = ConsentKey.fromString(code); // May throw here
36+
return Optional.of(key);
37+
} else {
38+
throw new IllegalArgumentException("Einwilligung found but no valid consent key present");
4039
}
4140
}
4241
}
Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,17 @@
11
package de.medizininformatikinitiative.torch.model.crtdl.annotated;
22

33
import com.fasterxml.jackson.databind.JsonNode;
4-
import org.slf4j.Logger;
5-
import org.slf4j.LoggerFactory;
4+
import de.medizininformatikinitiative.torch.model.mapping.ConsentKey;
65

76
import java.util.Optional;
87

98
import static java.util.Objects.requireNonNull;
109

11-
public record AnnotatedCrtdl(JsonNode cohortDefinition, AnnotatedDataExtraction dataExtraction) {
10+
public record AnnotatedCrtdl(JsonNode cohortDefinition, AnnotatedDataExtraction dataExtraction,
11+
Optional<ConsentKey> consentKey) {
1212

1313
public AnnotatedCrtdl {
1414
requireNonNull(cohortDefinition);
1515
requireNonNull(dataExtraction);
1616
}
17-
18-
public Optional<String> consentKey() {
19-
JsonNode inclusionCriteria = cohortDefinition.get("inclusionCriteria");
20-
if (inclusionCriteria != null && inclusionCriteria.isArray()) {
21-
for (JsonNode criteriaGroup : inclusionCriteria) {
22-
for (JsonNode criteria : criteriaGroup) {
23-
JsonNode context = criteria.get("context");
24-
if (context != null && "Einwilligung".equals(context.get("code").asText())) {
25-
JsonNode termcodes = criteria.get("termCodes");
26-
if (termcodes != null && termcodes.isArray()) {
27-
JsonNode firstTermcode = termcodes.get(0);
28-
if (firstTermcode != null && firstTermcode.has("code")) {
29-
return Optional.of(firstTermcode.get("code").asText());
30-
}
31-
}
32-
}
33-
}
34-
}
35-
}
36-
return Optional.empty();
37-
}
3817
}

src/main/java/de/medizininformatikinitiative/torch/model/mapping/ConsentKey.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,13 @@ public enum ConsentKey {
3131
public String toString() {
3232
return s;
3333
}
34+
35+
public static ConsentKey fromString(String value) {
36+
for (ConsentKey key : values()) {
37+
if (key.toString().equalsIgnoreCase(value)) {
38+
return key;
39+
}
40+
}
41+
throw new IllegalArgumentException("Unknown ConsentKey string: " + value);
42+
}
3443
}

src/main/java/de/medizininformatikinitiative/torch/service/CrtdlValidatorService.java

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
import org.slf4j.Logger;
1717
import org.slf4j.LoggerFactory;
1818

19-
import java.io.IOException;
20-
import java.util.*;
19+
import java.util.ArrayList;
20+
import java.util.HashSet;
21+
import java.util.List;
22+
import java.util.Objects;
23+
import java.util.Set;
2124
import java.util.function.Predicate;
2225

2326
public class CrtdlValidatorService {
@@ -29,7 +32,7 @@ public class CrtdlValidatorService {
2932
private final FilterService filterService;
3033

3134

32-
public CrtdlValidatorService(StructureDefinitionHandler profileHandler, StandardAttributeGenerator attributeGenerator, FilterService filterService) throws IOException {
35+
public CrtdlValidatorService(StructureDefinitionHandler profileHandler, StandardAttributeGenerator attributeGenerator, FilterService filterService) {
3336
this.profileHandler = profileHandler;
3437
this.attributeGenerator = attributeGenerator;
3538
this.filterService = filterService;
@@ -42,6 +45,11 @@ public CrtdlValidatorService(StructureDefinitionHandler profileHandler, Standard
4245
* @return the validated Crtdl or an error signal with ValidationException if a profile is unknown.
4346
*/
4447
public AnnotatedCrtdl validate(Crtdl crtdl) throws ValidationException {
48+
try {
49+
crtdl.consentKey();
50+
} catch (IllegalArgumentException e) {
51+
throw new ValidationException("No valid Consent Key Found");
52+
}
4553
List<AnnotatedAttributeGroup> annotatedAttributeGroups = new ArrayList<>();
4654
Set<String> linkedGroups = new HashSet<>();
4755
Set<String> successfullyAnnotatedGroups = new HashSet<>();
@@ -50,16 +58,13 @@ public AnnotatedCrtdl validate(Crtdl crtdl) throws ValidationException {
5058

5159
for (AttributeGroup attributeGroup : crtdl.dataExtraction().attributeGroups()) {
5260
StructureDefinition definition = profileHandler.getDefinition(attributeGroup.groupReference());
53-
if (definition != null) {
54-
if (Objects.equals(definition.getType(), "Patient")) {
55-
if (exactlyOnePatientGroup) {
56-
throw new ValidationException(" More than one Patient Attribute Group");
57-
} else {
58-
exactlyOnePatientGroup = true;
59-
patientAttributeGroupId = attributeGroup.id();
60-
logger.debug("Found Patient Attribute Group {}", patientAttributeGroupId);
61-
}
62-
61+
if (definition != null && Objects.equals(definition.getType(), "Patient")) {
62+
if (exactlyOnePatientGroup) {
63+
throw new ValidationException(" More than one Patient Attribute Group");
64+
} else {
65+
exactlyOnePatientGroup = true;
66+
patientAttributeGroupId = attributeGroup.id();
67+
logger.debug("Found Patient Attribute Group {}", patientAttributeGroupId);
6368
}
6469
}
6570
}
@@ -89,7 +94,7 @@ public AnnotatedCrtdl validate(Crtdl crtdl) throws ValidationException {
8994
}
9095

9196

92-
return new AnnotatedCrtdl(crtdl.cohortDefinition(), new AnnotatedDataExtraction(annotatedAttributeGroups));
97+
return new AnnotatedCrtdl(crtdl.cohortDefinition(), new AnnotatedDataExtraction(annotatedAttributeGroups), crtdl.consentKey());
9398
}
9499

95100
private AnnotatedAttributeGroup annotateGroup(AttributeGroup attributeGroup, StructureDefinition

src/test/java/de/medizininformatikinitiative/torch/ConsentFetcherIT.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import de.medizininformatikinitiative.torch.exceptions.ConsentViolatedException;
66
import de.medizininformatikinitiative.torch.model.consent.PatientBatchWithConsent;
77
import de.medizininformatikinitiative.torch.model.management.PatientBatch;
8+
import de.medizininformatikinitiative.torch.model.mapping.ConsentKey;
89
import org.hl7.fhir.r4.model.DateTimeType;
910
import org.hl7.fhir.r4.model.Observation;
1011
import org.hl7.fhir.r4.model.Reference;
@@ -69,7 +70,7 @@ private void assertConsentFalse(PatientBatchWithConsent batch, String patientId,
6970

7071
@Test
7172
void failsOnNoPatientMatchesConsentKeyBuildingConsent() {
72-
var resultBatch = consentFetcher.buildConsentInfo("yes-no-no-yes", BATCH);
73+
var resultBatch = consentFetcher.buildConsentInfo(ConsentKey.YES_NO_NO_YES, BATCH);
7374

7475
StepVerifier.create(resultBatch)
7576
.expectErrorSatisfies(error -> assertThat(error)
@@ -80,7 +81,7 @@ void failsOnNoPatientMatchesConsentKeyBuildingConsent() {
8081

8182
@Test
8283
void failsOnUnknownPatientBuildingConsent() {
83-
var resultBatch = consentFetcher.buildConsentInfo("yes-yes-yes-yes", BATCH_UNKNOWN);
84+
var resultBatch = consentFetcher.buildConsentInfo(ConsentKey.YES_YES_YES_YES, BATCH_UNKNOWN);
8485

8586
StepVerifier.create(resultBatch)
8687
.expectErrorSatisfies(error -> assertThat(error)
@@ -91,7 +92,7 @@ void failsOnUnknownPatientBuildingConsent() {
9192

9293
@Test
9394
void successBuildingConsent() {
94-
var resultBatch = consentFetcher.buildConsentInfo("yes-yes-yes-yes", BATCH);
95+
var resultBatch = consentFetcher.buildConsentInfo(ConsentKey.YES_YES_YES_YES, BATCH);
9596

9697
StepVerifier.create(resultBatch)
9798
.assertNext(batch -> {

src/test/java/de/medizininformatikinitiative/torch/ConsentHandlerIT.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import de.medizininformatikinitiative.torch.consent.ConsentValidator;
55
import de.medizininformatikinitiative.torch.model.consent.PatientBatchWithConsent;
66
import de.medizininformatikinitiative.torch.model.management.PatientBatch;
7+
import de.medizininformatikinitiative.torch.model.mapping.ConsentKey;
78
import org.hl7.fhir.r4.model.DateTimeType;
89
import org.hl7.fhir.r4.model.Observation;
910
import org.hl7.fhir.r4.model.Reference;
@@ -67,7 +68,7 @@ private void assertConsentFalse(PatientBatchWithConsent batch, String patientId,
6768

6869
@Test
6970
void successAfterEncounterUpdatesProvisions() {
70-
var resultBatch = consentHandler.fetchAndBuildConsentInfo("yes-yes-yes-yes", BATCH);
71+
var resultBatch = consentHandler.fetchAndBuildConsentInfo(ConsentKey.YES_YES_YES_YES, BATCH);
7172

7273
StepVerifier.create(resultBatch)
7374
.assertNext(batch -> {

src/test/java/de/medizininformatikinitiative/torch/ConsentHandlerTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import de.medizininformatikinitiative.torch.consent.ConsentHandler;
55
import de.medizininformatikinitiative.torch.exceptions.ConsentViolatedException;
66
import de.medizininformatikinitiative.torch.model.management.PatientBatch;
7+
import de.medizininformatikinitiative.torch.model.mapping.ConsentKey;
78
import de.medizininformatikinitiative.torch.service.DataStore;
89
import org.junit.jupiter.api.Test;
910
import org.mockito.Mockito;
@@ -26,10 +27,10 @@ public class ConsentHandlerTest {
2627

2728
@Test
2829
void failsOnNoPatientMatchesConsentKeyBuildingConsent() {
29-
when(consentFetcher.buildConsentInfo("yes-no-no-yes", BATCH))
30+
when(consentFetcher.buildConsentInfo(ConsentKey.YES_NO_NO_YES, BATCH))
3031
.thenReturn(Mono.error(new ConsentViolatedException("No valid provisions found for any patients in batch")));
3132

32-
var resultBatch = consentHandler.fetchAndBuildConsentInfo("yes-no-no-yes", BATCH);
33+
var resultBatch = consentHandler.fetchAndBuildConsentInfo(ConsentKey.YES_NO_NO_YES, BATCH);
3334

3435
StepVerifier.create(resultBatch)
3536
.expectErrorSatisfies(error -> assertThat(error)
@@ -40,10 +41,10 @@ void failsOnNoPatientMatchesConsentKeyBuildingConsent() {
4041

4142
@Test
4243
void failsOnUnknownPatientBuildingConsent() {
43-
when(consentFetcher.buildConsentInfo("yes-yes-yes-yes", BATCH_UNKNOWN))
44+
when(consentFetcher.buildConsentInfo(ConsentKey.YES_YES_YES_YES, BATCH_UNKNOWN))
4445
.thenReturn(Mono.error(new ConsentViolatedException("No valid provisions found for any patients in batch")));
4546

46-
var resultBatch = consentHandler.fetchAndBuildConsentInfo("yes-yes-yes-yes", BATCH_UNKNOWN);
47+
var resultBatch = consentHandler.fetchAndBuildConsentInfo(ConsentKey.YES_YES_YES_YES, BATCH_UNKNOWN);
4748

4849
StepVerifier.create(resultBatch)
4950
.expectErrorSatisfies(error -> assertThat(error)

0 commit comments

Comments
 (0)