ConceptMap utilities

This commit is contained in:
Grahame Grieve 2024-03-05 20:10:13 +11:00
parent 7ba1511748
commit 0076a2206d
4 changed files with 179 additions and 69 deletions

View File

@ -300,7 +300,6 @@ public class NpmPackageVersionConverter {
} }
throw new Error("Unknown version " + currentVersion + " -> " + version); throw new Error("Unknown version " + currentVersion + " -> " + version);
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace();
errors.add("Error converting " + n + ": " + ex.getMessage()); errors.add("Error converting " + n + ": " + ex.getMessage());
return null; return null;
} }
@ -336,6 +335,7 @@ public class NpmPackageVersionConverter {
} }
} }
private void convertResourceR5(Resource res) { private void convertResourceR5(Resource res) {
if (res instanceof ImplementationGuide) { if (res instanceof ImplementationGuide) {
ImplementationGuide ig = (ImplementationGuide) res; ImplementationGuide ig = (ImplementationGuide) res;

View File

@ -629,5 +629,9 @@ public class Coding extends DataType implements IBaseCoding, ICompositeType, ICo
} }
// end addition // end addition
public String getVersionedSystem() {
return hasVersion() ? getSystem()+"|"+getVersion() : getSystem();
}
} }

View File

@ -1785,6 +1785,7 @@ public class ConceptMap extends MetadataResource {
} }
return addElement().setCode(code); return addElement().setCode(code);
} }
} }
@Block() @Block()
@ -2273,6 +2274,21 @@ public class ConceptMap extends MetadataResource {
} }
public boolean hasTargetCode(String code) {
for (TargetElementComponent tgt : getTarget()) {
if (code.equals(tgt.getCode())) {
return true;
}
}
return false;
}
public TargetElementComponent addTarget(String code, ConceptMapRelationship relationship) {
TargetElementComponent tgt = addTarget();
tgt.setCode(code);
tgt.setRelationship(relationship);
return tgt;
}
} }
@Block() @Block()
@ -8590,7 +8606,31 @@ public class ConceptMap extends MetadataResource {
private String tail(String uri) { private String tail(String uri) {
return uri.contains("/") ? uri.substring(uri.lastIndexOf("/")+1) : uri; return uri.contains("/") ? uri.substring(uri.lastIndexOf("/")+1) : uri;
} }
public ConceptMapGroupComponent getGroup(String su, String tu) {
for (ConceptMapGroupComponent g : getGroup()) {
if (su.equals(g.getSource()) && tu.equals(g.getTarget())) {
return g;
}
}
return null;
}
public ConceptMapGroupComponent forceGroup(String su, String tu) {
for (ConceptMapGroupComponent g : getGroup()) {
if (su.equals(g.getSource()) && tu.equals(g.getTarget())) {
return g;
}
}
ConceptMapGroupComponent g = addGroup();
g.setSource(su);
g.setTarget(tu);
return g;
}
// end addition // end addition
} }

View File

@ -322,92 +322,131 @@ public class ConceptMapUtilities {
return null; return null;
} }
public static boolean checkReciprocal(ConceptMap left, ConceptMap right, List<String> issues) { public static boolean checkReciprocal(ConceptMap left, ConceptMap right, List<String> issues, boolean makeChanges) {
boolean changed = false;
if (!Base.compareDeep(left.getTargetScope(), right.getSourceScope(), true)) { if (!Base.compareDeep(left.getTargetScope(), right.getSourceScope(), true)) {
issues.add("scopes are not reciprocal: "+left.getTargetScope()+" vs "+right.getSourceScope()); issues.add("scopes are not reciprocal: "+left.getTargetScope()+" vs "+right.getSourceScope());
} }
if (!Base.compareDeep(left.getSourceScope(), right.getTargetScope(), true)) { if (!Base.compareDeep(left.getSourceScope(), right.getTargetScope(), true)) {
issues.add("scopes are not reciprocal: "+left.getSourceScope()+" vs "+right.getTargetScope()); issues.add("scopes are not reciprocal: "+left.getSourceScope()+" vs "+right.getTargetScope());
} }
if (left.getGroup().size() != right.getGroup().size()) {
issues.add("group count mismatch: "+left.getGroup().size()+" vs "+right.getGroup().size());
}
for (ConceptMapGroupComponent gl : left.getGroup()) { for (ConceptMapGroupComponent gl : left.getGroup()) {
ConceptMapGroupComponent gr = findMatchingGroup(right.getGroup(), gl.getTarget(), gl.getSource()); ConceptMapGroupComponent gr = findMatchingGroup(right.getGroup(), gl.getTarget(), gl.getSource());
if (gr == null) { if (gr == null) {
issues.add("left maps from "+gl.getSource()+" to "+gl.getTarget()+" but right has no matching reverse map"); for (SourceElementComponent e : gl.getElement()) {
for (TargetElementComponent t : e.getTarget()) {
if (t.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) {
if (makeChanges) {
changed = true;
right.forceGroup(gl.getTarget(), gl.getSource()).getOrAddElement(t.getCode()).addTarget(e.getCode(), inverse(t.getRelationship()));
} else {
issues.add("left maps from "+gl.getSource()+"#"+e.getCode()+" to "+gl.getTarget()+"#"+t.getCode()+" but right has no matching reverse map");
}
}
}
}
} else { } else {
for (SourceElementComponent srcL : gl.getElement()) { for (SourceElementComponent srcL : gl.getElement()) {
if (!"CHECK!".equals(srcL.getCode())) { if (!srcL.getNoMap()) {
if (!srcL.getNoMap()) { for (TargetElementComponent tgtL : srcL.getTarget()) {
for (TargetElementComponent tgtL : srcL.getTarget()) { List<ElementMappingPair> pairs = getMappings(gr, tgtL.getCode(), srcL.getCode());
List<ElementMappingPair> pairs = getMappings(gr, tgtL.getCode(), srcL.getCode()); if (tgtL.getRelationship() == null) {
switch (tgtL.getRelationship()) { issues.add("Left map has relationship "+srcL.getCode()+" with no relationship");
case EQUIVALENT: } else switch (tgtL.getRelationship()) {
if (pairs.isEmpty()) { case EQUIVALENT:
if (pairs.isEmpty()) {
if (makeChanges) {
changed = true;
gr.getOrAddElement(tgtL.getCode()).addTarget(srcL.getCode(), ConceptMapRelationship.EQUIVALENT);
} else {
issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but there's no reverse relationship"); issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but there's no reverse relationship");
} else for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) {
issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
}
} }
break; } else for (ElementMappingPair pair : pairs) {
case RELATEDTO: if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) {
if (pairs.isEmpty()) { issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
issues.add("Left map says that "+srcL.getCode()+" is related to "+tgtL.getCode()+" but there's no reverse relationship");
} else for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT && pair.tgt.getRelationship() != ConceptMapRelationship.RELATEDTO) {
issues.add("Left map says that "+srcL.getCode()+" is related to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
}
} }
break;
case SOURCEISBROADERTHANTARGET:
if (pairs.isEmpty()) {
issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but there's no reverse relationship");
} else for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) {
issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
}
}
break;
case SOURCEISNARROWERTHANTARGET:
if (pairs.isEmpty()) {
issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but there's no reverse relationship");
} else for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISBROADERTHANTARGET) {
issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
}
}
break;
case NOTRELATEDTO:
for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) {
issues.add("Left map says that "+srcL.getCode()+" is not related to "+tgtL.getCode()+" but a reverse relationship exists with type "+pair.tgt.getRelationship().toCode());
}
}
break;
} }
} break;
} else { case RELATEDTO:
for (SourceElementComponent srcR : gr.getElement()) { if (pairs.isEmpty()) {
for (TargetElementComponent tgtR : srcR.getTarget()) { issues.add("Left map says that "+srcL.getCode()+" is related to "+tgtL.getCode()+" but there's no reverse relationship");
if (srcL.getCode().equals(tgtR.getCode())) { } else for (ElementMappingPair pair : pairs) {
issues.add("Left map says that there is no relationship for "+srcL.getCode()+" but right map has a "+tgtR.getRelationship().toCode()+" mapping to it from "+srcR.getCode()); if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT && pair.tgt.getRelationship() != ConceptMapRelationship.RELATEDTO) {
issues.add("Left map says that "+srcL.getCode()+" is related to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
} }
} }
break;
case SOURCEISBROADERTHANTARGET:
if (pairs.isEmpty()) {
issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but there's no reverse relationship");
} else for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) {
issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
}
}
break;
case SOURCEISNARROWERTHANTARGET:
if (pairs.isEmpty()) {
issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but there's no reverse relationship");
} else for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISBROADERTHANTARGET) {
issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
}
}
break;
case NOTRELATEDTO:
for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) {
issues.add("Left map says that "+srcL.getCode()+" is not related to "+tgtL.getCode()+" but a reverse relationship exists with type "+pair.tgt.getRelationship().toCode());
}
}
break;
}
}
} else {
for (SourceElementComponent srcR : gr.getElement()) {
for (TargetElementComponent tgtR : srcR.getTarget()) {
if (srcL.getCode().equals(tgtR.getCode())) {
issues.add("Left map says that there is no relationship for "+srcL.getCode()+" but right map has a "+tgtR.getRelationship().toCode()+" mapping to it from "+srcR.getCode());
}
} }
} }
} }
} }
}
}
for (ConceptMapGroupComponent gr : right.getGroup()) {
ConceptMapGroupComponent gl = findMatchingGroup(left.getGroup(), gr.getTarget(), gr.getSource());
if (gl == null) {
for (SourceElementComponent e : gr.getElement()) {
for (TargetElementComponent t : e.getTarget()) {
if (t.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) {
if (makeChanges) {
changed = true;
left.forceGroup(gr.getTarget(), gr.getSource()).getOrAddElement(t.getCode()).addTarget(e.getCode(), inverse(t.getRelationship()));
} else {
issues.add("left maps from "+gr.getSource()+"#"+e.getCode()+" to "+gr.getTarget()+"#"+t.getCode()+" but right has no matching reverse map");
}
}
}
}
} else {
for (SourceElementComponent srcR : gr.getElement()) { for (SourceElementComponent srcR : gr.getElement()) {
if (!"CHECK!".equals(srcR.getCode())) { if (!"CHECK!".equals(srcR.getCode())) {
if (!srcR.getNoMap()) { if (!srcR.getNoMap()) {
for (TargetElementComponent tgtR : srcR.getTarget()) { for (TargetElementComponent tgtR : srcR.getTarget()) {
List<ElementMappingPair> pairs = getMappings(gl, tgtR.getCode(), srcR.getCode()); List<ElementMappingPair> pairs = getMappings(gl, tgtR.getCode(), srcR.getCode());
switch (tgtR.getRelationship()) { if (tgtR.getRelationship() == null) {
issues.add("Right map has relationship "+srcR.getCode()+" with no relationship");
} else switch (tgtR.getRelationship()) {
case EQUIVALENT: case EQUIVALENT:
if (pairs.isEmpty()) { if (pairs.isEmpty()) {
issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but there's no reverse relationship"); if (makeChanges) {
changed = true;
gl.getOrAddElement(tgtR.getCode()).addTarget(srcR.getCode(), ConceptMapRelationship.EQUIVALENT);
} else {
issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but there's no reverse relationship");
}
} else for (ElementMappingPair pair : pairs) { } else for (ElementMappingPair pair : pairs) {
if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) { if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) {
issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
@ -463,6 +502,27 @@ public class ConceptMapUtilities {
} }
} }
} }
return changed;
}
private static ConceptMapRelationship inverse(ConceptMapRelationship relationship) {
switch (relationship) {
case EQUIVALENT: return ConceptMapRelationship.EQUIVALENT;
case RELATEDTO: return ConceptMapRelationship.RELATEDTO;
case SOURCEISBROADERTHANTARGET: return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET;
case SOURCEISNARROWERTHANTARGET: return ConceptMapRelationship.SOURCEISBROADERTHANTARGET;
default: return null;
}
}
private static boolean hasActualMappings(ConceptMapGroupComponent gr) {
for (SourceElementComponent e : gr.getElement()) {
for (TargetElementComponent tgt : e.getTarget()) {
if (tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) {
return true;
}
}
}
return false; return false;
} }
@ -523,28 +583,34 @@ public class ConceptMapUtilities {
return i; return i;
} }
public static Set<String> listCodesWithNoMappings(Set<String> set, ConceptMap map) { public static Set<Coding> listCodesWithNoMappings(Set<Coding> codes, ConceptMap map) {
Set<String> res = new HashSet<>(); Set<Coding> res = new HashSet<>();
for (String s : set) { for (Coding c : codes) {
if (s != null) { if (c != null && c.hasCode()) {
boolean found = false; boolean found = false;
for (ConceptMapGroupComponent grp : map.getGroup()) { for (ConceptMapGroupComponent grp : map.getGroup()) {
for (SourceElementComponent src : grp.getElement()) { if (matchesCoding(grp, c)) {
if (s.equals(src.getCode())) { for (SourceElementComponent src : grp.getElement()) {
for (TargetElementComponent tgt : src.getTarget()) { if (c.getCode().equals(src.getCode())) {
if (tgt.getRelationship() == ConceptMapRelationship.RELATEDTO || tgt.getRelationship() == ConceptMapRelationship.EQUIVALENT || tgt.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { for (TargetElementComponent tgt : src.getTarget()) {
found = true; if (tgt.getRelationship() == ConceptMapRelationship.RELATEDTO || tgt.getRelationship() == ConceptMapRelationship.EQUIVALENT || tgt.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) {
found = true;
}
} }
} }
} }
} }
} }
if (!found) { if (!found) {
res.add(s); res.add(c);
} }
} }
} }
return res; return res;
} }
private static boolean matchesCoding(ConceptMapGroupComponent grp, Coding code) {
return code.getSystem().equals(grp.getSource()) || (code.getSystem()+"|"+code.getVersion()).equals(grp.getSource());
}
} }