Skip to content

Commit

Permalink
Update Mapping Tree
Browse files Browse the repository at this point in the history
Fixes: #151
  • Loading branch information
bastianschaffer committed Sep 3, 2024
1 parent 2022a45 commit b239112
Show file tree
Hide file tree
Showing 13 changed files with 432 additions and 273 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<hapi-fhir.version>7.2.1</hapi-fhir.version>
<testcontainers.version>1.20.0</testcontainers.version>
<slf4j.version>2.0.13</slf4j.version>
<ontology.version>2.2.0</ontology.version>
<ontology.version>3.0.0-test.1</ontology.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -177,7 +177,7 @@
<goal>download-single</goal>
</goals>
<configuration>
<url>https://github.com/medizininformatik-initiative/fhir-ontology-generator/raw/v${ontology.version}/example/mii_core_data_set/ontology/mapping.zip</url>
<url>https://github.com/medizininformatik-initiative/fhir-ontology-generator/raw/v${ontology.version}/example/fdpg-ontology/mapping.zip</url>
<toDir>${project.build.directory}</toDir>
</configuration>
</execution>
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/de/numcodex/sq2cql/model/MappingContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
public class MappingContext {

private final Map<ContextualTermCode, Mapping> mappings;
private final TermCodeNode conceptTree;
private final MappingTreeBase conceptTree;
private final Map<String, CodeSystemDefinition> codeSystemDefinitions;

private MappingContext(Map<ContextualTermCode, Mapping> mappings, TermCodeNode conceptTree,
private MappingContext(Map<ContextualTermCode, Mapping> mappings, MappingTreeBase conceptTree,
Map<String, CodeSystemDefinition> codeSystemDefinitions) {
this.mappings = mappings;
this.conceptTree = conceptTree;
Expand All @@ -48,7 +48,7 @@ public static MappingContext of() {
* @param codeSystemAliases a map of code system URLs to their aliases
* @return the mapping context
*/
public static MappingContext of(Map<ContextualTermCode, Mapping> mappings, TermCodeNode conceptTree,
public static MappingContext of(Map<ContextualTermCode, Mapping> mappings, MappingTreeBase conceptTree,
Map<String, String> codeSystemAliases) {
return new MappingContext(Map.copyOf(mappings), conceptTree, codeSystemAliases.entrySet().stream()
.collect(Collectors.toConcurrentMap(Map.Entry::getKey,
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/de/numcodex/sq2cql/model/MappingTreeBase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.numcodex.sq2cql.model;


import de.numcodex.sq2cql.model.structured_query.ContextualTermCode;

import java.util.List;
import java.util.stream.Stream;

public record MappingTreeBase(List<MappingTreeModuleRoot> moduleRoots) {

public Stream<ContextualTermCode> expand(ContextualTermCode termCode) {
var key = termCode.termCode().code();

return moduleRoots.stream().flatMap(moduleRoot ->
moduleRoot.isModuleMatching(termCode) ? moduleRoot.expand(key) : Stream.empty());
}
}
16 changes: 16 additions & 0 deletions src/main/java/de/numcodex/sq2cql/model/MappingTreeModuleEntry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package de.numcodex.sq2cql.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

@JsonIgnoreProperties(ignoreUnknown = true)
public record MappingTreeModuleEntry(String key, List<String> children) {
@JsonCreator
static MappingTreeModuleEntry fromJson(@JsonProperty("key") String key,
@JsonProperty("children") List<String> children) {
return new MappingTreeModuleEntry(key, children);
}
}
40 changes: 40 additions & 0 deletions src/main/java/de/numcodex/sq2cql/model/MappingTreeModuleRoot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package de.numcodex.sq2cql.model;


import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.numcodex.sq2cql.model.common.TermCode;
import de.numcodex.sq2cql.model.structured_query.ContextualTermCode;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.function.Function.identity;

@JsonIgnoreProperties(ignoreUnknown = true)
public record MappingTreeModuleRoot(TermCode context, String system, Map<String, MappingTreeModuleEntry> entries) {
@JsonCreator
static MappingTreeModuleRoot fromJson(@JsonProperty("context") TermCode context,
@JsonProperty("system") String system,
@JsonProperty("entries") List<MappingTreeModuleEntry> entries) {
return new MappingTreeModuleRoot(
context,
system,
entries.stream().collect(Collectors.toMap(MappingTreeModuleEntry::key, identity())));
}

public Stream<ContextualTermCode> expand(String key) {
var newTermCode = new ContextualTermCode(context, new TermCode(system, key, ""));

return Stream.concat(Stream.of(newTermCode), entries.get(key).children().stream().flatMap(this::expand));
}

boolean isModuleMatching(ContextualTermCode contextualTermCode) {
return context.equals(contextualTermCode.context()) &&
system.equals(contextualTermCode.termCode().system()) &&
entries.containsKey(contextualTermCode.termCode().code());
}
}
61 changes: 0 additions & 61 deletions src/main/java/de/numcodex/sq2cql/model/TermCodeNode.java

This file was deleted.

14 changes: 7 additions & 7 deletions src/test/java/de/numcodex/sq2cql/EvaluationIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.StringParam;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.numcodex.sq2cql.model.Mapping;
import de.numcodex.sq2cql.model.MappingContext;
import de.numcodex.sq2cql.model.TermCodeNode;
import de.numcodex.sq2cql.model.*;
import de.numcodex.sq2cql.model.common.TermCode;
import de.numcodex.sq2cql.model.structured_query.ContextualConcept;
import de.numcodex.sq2cql.model.structured_query.ContextualTermCode;
Expand All @@ -31,6 +29,7 @@
import java.util.Map;
import java.util.UUID;

import static de.numcodex.sq2cql.Util.createTreeWithoutChildren;
import static de.numcodex.sq2cql.model.common.Comparator.LESS_THAN;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
Expand All @@ -42,8 +41,9 @@
public class EvaluationIT {

static final TermCode CONTEXT = TermCode.of("context", "context", "context");
static final ContextualTermCode ROOT = ContextualTermCode.of(CONTEXT, TermCode.of("", "", ""));
static final ContextualTermCode BLOOD_PRESSURE = ContextualTermCode.of(CONTEXT, TermCode.of("http://loinc.org", "85354-9",
static final String BLOOD_PRESSURE_CODE = "85354-9";
static final String BLOOD_PRESSURE_SYSTEM = "http://loinc.org";
static final ContextualTermCode BLOOD_PRESSURE = ContextualTermCode.of(CONTEXT, TermCode.of(BLOOD_PRESSURE_SYSTEM, BLOOD_PRESSURE_CODE,
"Blood pressure panel with all children optional"));
static final TermCode DIASTOLIC_BLOOD_PRESSURE = TermCode.of("http://loinc.org", "8462-4",
"Diastolic blood pressure");
Expand Down Expand Up @@ -99,7 +99,7 @@ public void evaluateBloodPressure() throws Exception {
var valueFhirPath = format("component.where(code.coding.exists(system = '%s' and code = '%s')).value.first()",
DIASTOLIC_BLOOD_PRESSURE.system(), DIASTOLIC_BLOOD_PRESSURE.code());
var mappings = Map.of(BLOOD_PRESSURE, Mapping.of(BLOOD_PRESSURE, "Observation", valueFhirPath));
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(BLOOD_PRESSURE));
var conceptTree = createTreeWithoutChildren(BLOOD_PRESSURE);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);
var translator = Translator.of(mappingContext);
var criterion = NumericCriterion.of(ContextualConcept.of(BLOOD_PRESSURE), LESS_THAN, BigDecimal.valueOf(80), "mm[Hg]");
Expand Down Expand Up @@ -157,7 +157,7 @@ public void evaluateBloodPressureAttribute() throws Exception {
}
""");
var mappings = Map.of(BLOOD_PRESSURE, mapping);
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(BLOOD_PRESSURE));
var conceptTree = createTreeWithoutChildren(BLOOD_PRESSURE);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);
var translator = Translator.of(mappingContext);
var structuredQuery = readStructuredQuery("""
Expand Down
41 changes: 21 additions & 20 deletions src/test/java/de/numcodex/sq2cql/TranslatorTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package de.numcodex.sq2cql;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.numcodex.sq2cql.model.AttributeMapping;
import de.numcodex.sq2cql.model.Mapping;
import de.numcodex.sq2cql.model.MappingContext;
import de.numcodex.sq2cql.model.TermCodeNode;
import de.numcodex.sq2cql.model.*;
import de.numcodex.sq2cql.model.common.TermCode;
import de.numcodex.sq2cql.model.structured_query.*;
import org.junit.jupiter.api.Nested;
Expand All @@ -15,6 +12,7 @@
import java.util.Map;

import static de.numcodex.sq2cql.Assertions.assertThat;
import static de.numcodex.sq2cql.Util.*;
import static de.numcodex.sq2cql.model.common.Comparator.LESS_THAN;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down Expand Up @@ -118,7 +116,7 @@ void nonExpandableConcept() {

@Test
void nonMappableConcept() {
var conceptTree = TermCodeNode.of(C71, TermCodeNode.of(C71_0), TermCodeNode.of(C71_1));
var conceptTree = createTreeWithChildren(C71, C71_0, C71_1);
var mappingContext = MappingContext.of(Map.of(), conceptTree, CODE_SYSTEM_ALIASES);

var message = assertThrows(TranslationException.class, () -> Translator.of(mappingContext)
Expand All @@ -136,7 +134,7 @@ void usage_Documentation() {
TermCode.of("http://fhir.de/CodeSystem/bfarm/icd-10-gm", "C71.1",
"Malignant neoplasm of brain"));
var mappings = Map.of(c71_1, Mapping.of(c71_1, "Condition"));
var conceptTree = TermCodeNode.of(c71_1);
var conceptTree = createTreeWithoutChildren(c71_1);
var codeSystemAliases = Map.of("http://fhir.de/CodeSystem/bfarm/icd-10-gm", "icd10");
var mappingContext = MappingContext.of(mappings, conceptTree, codeSystemAliases);

Expand Down Expand Up @@ -167,7 +165,7 @@ void timeRestriction() {
"Malignant neoplasm of brain"));
var mappings = Map.of(c71_1,
Mapping.of(c71_1, "Condition", null, null, List.of(), List.of(), "onset"));
var conceptTree = TermCodeNode.of(c71_1);
var conceptTree = createTreeWithoutChildren(c71_1);
var codeSystemAliases = Map.of("http://fhir.de/CodeSystem/bfarm/icd-10-gm", "icd10");
var mappingContext = MappingContext.of(mappings, conceptTree, codeSystemAliases);

Expand Down Expand Up @@ -201,7 +199,7 @@ void timeRestriction_missingPathInMapping() {
"Malignant neoplasm of brain"));
var mappings = Map.of(c71_1,
Mapping.of(c71_1, "Condition", null, null, List.of(), List.of(), null));
var conceptTree = TermCodeNode.of(c71_1);
var conceptTree = createTreeWithoutChildren(c71_1);
var codeSystemAliases = Map.of("http://fhir.de/CodeSystem/bfarm/icd-10-gm", "icd10");
var mappingContext = MappingContext.of(mappings, conceptTree, codeSystemAliases);
var query = StructuredQuery.of(List.of(List.of(ConceptCriterion.of(ContextualConcept.of(c71_1),
Expand All @@ -220,8 +218,9 @@ void test_Task1() {
Mapping.of(C71_1, "Condition", null, null, List.of(),
List.of(VERIFICATION_STATUS_ATTR_MAPPING)), TMZ,
Mapping.of(TMZ, "MedicationStatement"));
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(TMZ),
TermCodeNode.of(C71, TermCodeNode.of(C71_0), TermCodeNode.of(C71_1)));
var conceptTree = new MappingTreeBase(List.of(
createTreeRootWithoutChildren(TMZ),
createTreeRootWithChildren(C71, C71_0, C71_1)));
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);
var structuredQuery = StructuredQuery.of(List.of(List.of(
ConceptCriterion.of(ContextualConcept.of(C71))
Expand Down Expand Up @@ -270,8 +269,10 @@ void test_Task2() {
Mapping.of(HYPERTENSION, "Condition", null, null, List.of(),
List.of(VERIFICATION_STATUS_ATTR_MAPPING)), SERUM, Mapping.of(SERUM, "Specimen"), LIPID,
Mapping.of(LIPID, "MedicationStatement"));
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(HYPERTENSION), TermCodeNode.of(SERUM),
TermCodeNode.of(LIPID));
var conceptTree = new MappingTreeBase(List.of(
createTreeRootWithoutChildren(HYPERTENSION),
createTreeRootWithoutChildren(SERUM),
createTreeRootWithoutChildren(LIPID)));
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);
var structuredQuery = StructuredQuery.of(List.of(List.of(
ConceptCriterion.of(ContextualConcept.of(HYPERTENSION))
Expand Down Expand Up @@ -324,7 +325,7 @@ void geccoTask2() {
Mapping.of(G47_31, "Condition", null, null,
List.of(CodingModifier.of("verificationStatus.coding", CONFIRMED)), List.of()),
TOBACCO_SMOKING_STATUS, Mapping.of(TOBACCO_SMOKING_STATUS, "Observation", "value"));
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(COPD), TermCodeNode.of(G47_31));
var conceptTree = new MappingTreeBase(List.of(createTreeRootWithoutChildren(COPD), createTreeRootWithoutChildren(G47_31)));
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);
var structuredQuery = StructuredQuery.of(
List.of(List.of(ValueSetCriterion.of(ContextualConcept.of(FRAILTY_SCORE), VERY_FIT, WELL))),
Expand Down Expand Up @@ -467,7 +468,7 @@ void onlyFixedCriteria() throws Exception {
}
""");

var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(COMBINED_CONSENT));
var conceptTree = createTreeWithoutChildren(COMBINED_CONSENT);
var mappings = Map.of(COMBINED_CONSENT, mapping);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);

Expand Down Expand Up @@ -549,7 +550,7 @@ void numericAgeTranslation() throws Exception {
]
}
""");
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(AGE));
var conceptTree = createTreeWithoutChildren(AGE);
var mappings = Map.of(AGE, mapping);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);

Expand Down Expand Up @@ -625,7 +626,7 @@ void ageRangeTranslation() throws Exception {
]
}
""");
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(AGE));
var conceptTree = createTreeWithoutChildren(AGE);
var mappings = Map.of(AGE, mapping);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);

Expand Down Expand Up @@ -701,7 +702,7 @@ void numericAgeTranslationInHours() throws Exception {
]
}
""");
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(AGE));
var conceptTree = createTreeWithoutChildren(AGE);
var mappings = Map.of(AGE, mapping);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);

Expand Down Expand Up @@ -777,7 +778,7 @@ void patientGender() throws Exception {
]
}
""");
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(GENDER));
var conceptTree = createTreeWithoutChildren(GENDER);
var mappings = Map.of(GENDER, mapping);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);

Expand Down Expand Up @@ -844,7 +845,7 @@ void consent() throws Exception {
]
}
""");
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(CONSENT_MDAT));
var conceptTree = createTreeWithoutChildren(CONSENT_MDAT);
var mappings = Map.of(CONSENT_MDAT, mapping);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);

Expand Down Expand Up @@ -938,7 +939,7 @@ void bloodPressure() throws Exception {
]
}
""");
var conceptTree = TermCodeNode.of(ROOT, TermCodeNode.of(BLOOD_PRESSURE));
var conceptTree = createTreeWithoutChildren(BLOOD_PRESSURE);
var mappings = Map.of(BLOOD_PRESSURE, mapping);
var mappingContext = MappingContext.of(mappings, conceptTree, CODE_SYSTEM_ALIASES);

Expand Down
Loading

0 comments on commit b239112

Please sign in to comment.