Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add term binding validation for IANA_media-type terminology #625

Merged
merged 7 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@

public class OpenEHRTerminologyAccess implements TerminologyAccess {

static volatile OpenEHRTerminologyAccess instance;
private static final Pattern openEHRTermIdPattern = Pattern.compile("http://openehr.org/id/(?<id>[0-9]+)");
private static final Pattern IANATermIdPattern = Pattern.compile("https://www.w3.org/ns/iana/media-types/(?<type>.+)/(?<subtype>.+)#Resource");

static volatile OpenEHRTerminologyAccess instance;
static boolean READ_FROM_JSON = true;

@JsonProperty
Expand Down Expand Up @@ -130,7 +132,15 @@
return Collections.emptyList();
}

private static final Pattern openEHRTermIdPattern = Pattern.compile("http://openehr.org/id/(?<id>[0-9]+)");
public String parseIANATerminologyURI(String uri) {
Matcher matcher = IANATermIdPattern.matcher(uri);
if(matcher.matches()) {
String type = matcher.group("type");
String subType = matcher.group("subtype");
return type + "/" + subType;
}
return null;

Check warning on line 142 in openehr-terminology/src/main/java/com/nedap/archie/terminology/OpenEHRTerminologyAccess.java

View check run for this annotation

Codecov / codecov/patch

openehr-terminology/src/main/java/com/nedap/archie/terminology/OpenEHRTerminologyAccess.java#L142

Added line #L142 was not covered by tests
}

public String parseTerminologyURI(String uri) {
Matcher matcher = openEHRTermIdPattern.matcher(uri);
Expand Down Expand Up @@ -191,7 +201,6 @@
.collect(Collectors.toList());
return codes.stream().filter(c -> c.getCodeString().equalsIgnoreCase(code)).findFirst().orElse(null);
}

}


Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@
values = terminologyCode.getValueSetExpanded();
} else if (terminologyId.equalsIgnoreCase("openehr")) {
values = getOpenEHRValueSetExpanded(terminologyCode);
} else if (terminologyId.equalsIgnoreCase("IANA_media-types")) {
values = getIANAMediaTypesValueSetExpanded(terminologyCode);
} else {
// This is not a local nor an openehr terminology.
// If a term binding is there, we may be able to validate, if external, we wil not be able to.
Expand Down Expand Up @@ -169,6 +171,29 @@
return result;
}

private List<String> getIANAMediaTypesValueSetExpanded(CTerminologyCode terminologyCode) {
List<String> atCodes = terminologyCode.getValueSetExpanded();
ArchetypeTerminology terminology = getTerminology(terminologyCode);
OpenEHRTerminologyAccess terminologyAccess = OpenEHRTerminologyAccess.getInstance();
List<String> result = new ArrayList<>();

if(terminology == null) {
return result;

Check warning on line 181 in tools/src/main/java/com/nedap/archie/rmobjectvalidator/PrimitiveObjectConstraintHelper.java

View check run for this annotation

Codecov / codecov/patch

tools/src/main/java/com/nedap/archie/rmobjectvalidator/PrimitiveObjectConstraintHelper.java#L181

Added line #L181 was not covered by tests
}

for (String atCode : atCodes) {
URI termBinding = terminology.getTermBinding("IANA_media-types", atCode);
if (termBinding != null) {
String value = terminologyAccess.parseIANATerminologyURI(termBinding.toString());
if (value != null) {
result.add(value);
}
}
}

return result;
}

private ArchetypeTerminology getTerminology(CTerminologyCode cTerminologyCode) {
Archetype archetype = cTerminologyCode.getArchetype();
if(archetype != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ private void handleMatches(AssertionResult assertionResult, int index, BinaryOpe
/// it would be better to check the actual type, but hasn't been done now
//this limits this to the OpenEHR RM
path = path + "/defining_code/code_string";
} else if (path.endsWith("defining_code")) {
} else {
path = path + "/code_string";
}
setPathsToValues(assertionResult, path, valueList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ public void setupArchetype() {
put("openehr", new HashMap<String, URI>() {{
put("at9000", URI.create("http://openehr.org/id/532"));
}});

put("IANA_media-types", new HashMap<String, URI>() {{
put("at9001", URI.create("https://www.w3.org/ns/iana/media-types/text/plain#Resource"));
}});
}});
archetype.setTerminology(terminology);

Expand Down Expand Up @@ -115,6 +119,26 @@ public void openEHRTerminology() {
assertFalse(validationHelper.isValidValue(code, null));
}

@Test
public void IANATerminology() {
CTerminologyCode code = new CTerminologyCode();
code.setParent(new DummyRulesPrimitiveObjectParent(archetype));
code.addConstraint("at9001");
code.setConstraintStatus(ConstraintStatus.REQUIRED);

DvCodedText text = new DvCodedText();
text.setValue("does not matter for this validation");
text.setDefiningCode(new CodePhrase("[IANA_media-types::text/plain]"));
assertTrue(validationHelper.isValidValue(code, text));
text.setDefiningCode(new CodePhrase("[IANA_media-types::abc/def]"));
assertFalse(validationHelper.isValidValue(code, text));
text.setDefiningCode(new CodePhrase());
assertFalse(validationHelper.isValidValue(code, text));
text.setDefiningCode(null);
assertFalse(validationHelper.isValidValue(code, text));
assertFalse(validationHelper.isValidValue(code, null));
}

@Test
public void terminologyCodeConstraint() {
CTerminologyCode code = new CTerminologyCode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.nedap.archie.rm.datastructures.Element;
import com.nedap.archie.rm.datatypes.CodePhrase;
import com.nedap.archie.rm.datavalues.DvCodedText;
import com.nedap.archie.rm.datavalues.encapsulated.DvMultimedia;
import com.nedap.archie.rm.datavalues.quantity.DvQuantity;
import com.nedap.archie.rm.support.identification.TerminologyId;
import com.nedap.archie.rminfo.ArchieRMInfoLookup;
Expand Down Expand Up @@ -818,25 +819,37 @@ public void termBindingConstraint() throws IOException, ADLParseException {
DvCodedText codedText = (DvCodedText) root.itemAtPath("items[id2]/value[id3]");
codedText.setDefiningCode(new CodePhrase(new TerminologyId("openehr"), "526"));
EvaluationResult evaluationResult = ruleEvaluation.evaluate(root, archetype.getRules().getRules());
assertEquals(2, evaluationResult.getAssertionResults().size());
assertEquals(3, evaluationResult.getAssertionResults().size());
assertTrue(evaluationResult.getAssertionResults().get(0).getResult());

codedText.setDefiningCode(new CodePhrase(new TerminologyId("openehr"), "527"));
evaluationResult = ruleEvaluation.evaluate(root, archetype.getRules().getRules());
assertEquals(2, evaluationResult.getAssertionResults().size());
assertEquals(3, evaluationResult.getAssertionResults().size());
assertFalse(evaluationResult.getAssertionResults().get(0).getResult());

// Rule with value set
DvCodedText codedText2 = (DvCodedText) root.itemAtPath("items[id4]/value[id5]");
codedText2.setDefiningCode(new CodePhrase(new TerminologyId("openehr"), "526"));
EvaluationResult evaluationResult2 = ruleEvaluation.evaluate(root, archetype.getRules().getRules());
assertEquals(2, evaluationResult2.getAssertionResults().size());
assertTrue(evaluationResult2.getAssertionResults().get(1).getResult());
evaluationResult = ruleEvaluation.evaluate(root, archetype.getRules().getRules());
assertEquals(3, evaluationResult.getAssertionResults().size());
assertTrue(evaluationResult.getAssertionResults().get(1).getResult());

codedText2.setDefiningCode(new CodePhrase(new TerminologyId("openehr"), "528"));
evaluationResult = ruleEvaluation.evaluate(root, archetype.getRules().getRules());
assertEquals(2, evaluationResult.getAssertionResults().size());
assertEquals(3, evaluationResult.getAssertionResults().size());
assertFalse(evaluationResult.getAssertionResults().get(1).getResult());

// Rule with external terminology term binding
DvMultimedia dvMultimedia = (DvMultimedia) root.itemAtPath("items[id6]/value[id7]");
dvMultimedia.setMediaType(new CodePhrase(new TerminologyId("IANA_media-types"), "image/jpeg"));
evaluationResult = ruleEvaluation.evaluate(root, archetype.getRules().getRules());
assertEquals(3, evaluationResult.getAssertionResults().size());
assertTrue(evaluationResult.getAssertionResults().get(2).getResult());

dvMultimedia.setMediaType(new CodePhrase(new TerminologyId("IANA_media-types"), "text/plain"));
evaluationResult = ruleEvaluation.evaluate(root, archetype.getRules().getRules());
assertEquals(3, evaluationResult.getAssertionResults().size());
assertFalse(evaluationResult.getAssertionResults().get(2).getResult());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ definition
DV_CODED_TEXT[id5]
}
}
ELEMENT[id6] occurrences matches {1} matches {
value matches {
DV_MULTIMEDIA[id7]
}
}
}
}

rules
/items[id2]/value/defining_code matches {[at9000]}
/items[id4]/value/defining_code matches {[ac3]}
/items[id6]/value/media_type matches {[at9008]}

terminology
term_bindings = <
Expand All @@ -53,6 +59,10 @@ terminology
["at9006"] = <http://openehr.org/id/531>
["at9007"] = <http://openehr.org/id/532>
>
["IANA_media-types"] = <
["at9008"] = <https://www.w3.org/ns/iana/media-types/image/jpeg#Resource>
["at9009"] = <https://www.w3.org/ns/iana/media-types/text/plain#Resource>
>
>
value_sets = <
["ac3"] = <
Expand Down