From 85a8807e7b4da8f6b88b9d00e9403f55a11a6022 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 10 Feb 2024 23:11:56 +1100 Subject: [PATCH] code --- .../fhir/r5/renderers/ValueSetRenderer.java | 23 +- .../r5/terminologies/ConceptMapUtilities.java | 203 ++++++++++++++++++ 2 files changed, 221 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index ec6a9f967..84e78d7a6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -82,6 +82,8 @@ public class ValueSetRenderer extends TerminologyRenderer { private static final int MAX_DESIGNATIONS_IN_LINE = 5; + private static final int MAX_BATCH_VALIDATION_SIZE = 1000; + private List renderingMaps = new ArrayList(); public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException { @@ -1431,12 +1433,23 @@ public class ValueSetRenderer extends TerminologyRenderer { } } if (!context.isNoSlowLookup() && !serverList.isEmpty()) { - getContext().getWorker().validateCodeBatch(getContext().getTerminologyServiceOptions(), serverList, null); - for (CodingValidationRequest vr : serverList) { - ConceptDefinitionComponent v = vr.getResult().asConceptDefinition(); - if (v != null) { - results.put(vr.getCoding().getCode(), v); + try { + // todo: split this into 10k batches + int i = 0; + while (serverList.size() > i) { + int len = Integer.min(serverList.size(), MAX_BATCH_VALIDATION_SIZE); + List list = serverList.subList(i, i+len); + i += len; + getContext().getWorker().validateCodeBatch(getContext().getTerminologyServiceOptions(), list, null); + for (CodingValidationRequest vr : list) { + ConceptDefinitionComponent v = vr.getResult().asConceptDefinition(); + if (v != null) { + results.put(vr.getCoding().getCode(), v); + } + } } + } catch (Exception e1) { + return null; } } return results; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ConceptMapUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ConceptMapUtilities.java index 698a7689c..1112ba288 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ConceptMapUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ConceptMapUtilities.java @@ -1,7 +1,11 @@ package org.hl7.fhir.r5.terminologies; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CodeSystem; @@ -10,6 +14,8 @@ import org.hl7.fhir.r5.model.ConceptMap; import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; +import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship; +import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.ConceptMapElementSorter; import org.hl7.fhir.r5.model.Identifier; import org.hl7.fhir.r5.model.Meta; import org.hl7.fhir.r5.model.UriType; @@ -17,6 +23,40 @@ import org.hl7.fhir.r5.model.ValueSet; public class ConceptMapUtilities { + public static class TranslatedCode { + private String code; + private ConceptMapRelationship relationship; + public TranslatedCode(String code, ConceptMapRelationship relationship) { + super(); + this.code = code; + this.relationship = relationship; + } + public String getCode() { + return code; + } + public ConceptMapRelationship getRelationship() { + return relationship; + } + + } + + public static class ConceptMapElementSorter implements Comparator { + + @Override + public int compare(SourceElementComponent o1, SourceElementComponent o2) { + return o1.getCode().compareTo(o2.getCode()); + } + + } + + public static class ConceptMapTargetElementSorter implements Comparator { + + @Override + public int compare(TargetElementComponent o1, TargetElementComponent o2) { + return o1.getCode().compareTo(o2.getCode()); + } + + } public static boolean hasOID(ConceptMap cm) { return getOID(cm) != null; } @@ -85,4 +125,167 @@ public class ConceptMapUtilities { return cm; } + public static ConceptMap invert(ConceptMap src, String id, String url, String name, boolean collate) { + ConceptMap dst = src.copy(); + dst.setId(id); + dst.setUrl(url); + dst.setName(name); + dst.getGroup().clear(); + dst.setSourceScope(src.getTargetScope()); + dst.setTargetScope(src.getSourceScope()); + for (ConceptMapGroupComponent gs : src.getGroup()) { + ConceptMapGroupComponent gd = dst.addGroup(); + gd.setTargetElement(gs.getSourceElement()); + gd.setSourceElement(gs.getTargetElement()); + Map dstMap = new HashMap<>(); + for (SourceElementComponent es : gs.getElement()) { + for (TargetElementComponent ts : es.getTarget()) { + SourceElementComponent ed = collate ? dstMap.get(ts.getCode()) : null; + if (ed == null) { + ed = gd.addElement(); + ed.setCodeElement(ts.getCodeElement()); + if (collate) { + dstMap.put(ed.getCode(), ed); + } + } + TargetElementComponent td = ed.addTarget(); + td.setCode(es.getCode()); + td.setComment(ts.getComment()); + td.setRelationship(invertRelationship(ts.getRelationship())); + } + } + } + return dst; + } + + private static ConceptMapRelationship invertRelationship(ConceptMapRelationship relationship) { + if (relationship == null) { + return null; + } + switch (relationship) { + case EQUIVALENT: + return ConceptMapRelationship.EQUIVALENT; + case NOTRELATEDTO: + return ConceptMapRelationship.NOTRELATEDTO; + case NULL: + return ConceptMapRelationship.NULL; + case RELATEDTO: + return ConceptMapRelationship.RELATEDTO; + case SOURCEISBROADERTHANTARGET: + return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; + case SOURCEISNARROWERTHANTARGET: + return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; + default: + return null; + } + } + + public static ConceptMap collapse(String id, String url, boolean cumulative, ConceptMap src, ConceptMap... sequence) { + ConceptMap res = src.copy(); + res.setId(id); + res.setUrl(url); + + for (ConceptMap cm : sequence) { + if (res.hasTargetScope() && src.hasTargetScope()) { + if (!cm.getSourceScope().equals(cm.getTargetScope())) { + throw new Error("Mismatch between seqeuntial concept maps: "); + } else { + res.setTargetScope(cm.getTargetScope()); + } + } else { + res.setTargetScope(null); + } + } + + for (ConceptMapGroupComponent gd : res.getGroup()) { + for (ConceptMap cm : sequence) { + for (ConceptMapGroupComponent gt : cm.getGroup()) { + if (gt.getSource().equals(gd.getTarget())) { + gd.setTarget(gt.getTarget()); + + List processed = new ArrayList(); + for (SourceElementComponent ed : gd.getElement()) { + List list = new ArrayList<>(); + list.addAll(ed.getTarget()); + ed.getTarget().clear(); + for (TargetElementComponent ts : list) { + for (SourceElementComponent et : gt.getElement()) { + if (et.getCode().equals(ed.getCode())) { + processed.add(et); + for (TargetElementComponent tt : et.getTarget()) { + ed.addTarget().setCode(tt.getCode()).setRelationship(combineRelationships(ts.getRelationship(), tt.getRelationship())); + } + } + } + } + if (ed.getTarget().isEmpty()) { + if (cumulative) { + ed.getTarget().addAll(list); + } else { + ed.setNoMap(true); + } + } + } + if (cumulative) { + for (SourceElementComponent et : gt.getElement()) { + if (!processed.contains(et)) { + gd.addElement(et.copy()); + } + } + } + } + Collections.sort(gt.getElement(), new ConceptMapElementSorter()); + for (SourceElementComponent e: gt.getElement()) { + Collections.sort(e.getTarget(), new ConceptMapTargetElementSorter()); + } + } + } + } + return res; + } + + public static ConceptMapRelationship combineRelationships(ConceptMapRelationship rel1, ConceptMapRelationship rel2) { + switch (rel1) { + case EQUIVALENT: + return rel2; + case NOTRELATEDTO: + return ConceptMapRelationship.NOTRELATEDTO; + case NULL: + return null; + case RELATEDTO: + return rel2; + case SOURCEISBROADERTHANTARGET: + switch (rel2) { + case EQUIVALENT: + return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; + case NOTRELATEDTO: + return ConceptMapRelationship.NOTRELATEDTO; + case NULL: + return null; + case RELATEDTO: + return ConceptMapRelationship.RELATEDTO; + case SOURCEISBROADERTHANTARGET: + return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; + case SOURCEISNARROWERTHANTARGET: + return ConceptMapRelationship.RELATEDTO; + } + case SOURCEISNARROWERTHANTARGET: + switch (rel2) { + case EQUIVALENT: + return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; + case NOTRELATEDTO: + return ConceptMapRelationship.NOTRELATEDTO; + case NULL: + return null; + case RELATEDTO: + return ConceptMapRelationship.RELATEDTO; + case SOURCEISBROADERTHANTARGET: + return ConceptMapRelationship.RELATEDTO; + case SOURCEISNARROWERTHANTARGET: + return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; + } + } + return null; + } + }