Skip to content

Commit

Permalink
Merge pull request #1883 from hapifhir/2025-01-gg-ai
Browse files Browse the repository at this point in the history
2025 01 gg ai
  • Loading branch information
grahamegrieve authored Jan 16, 2025
2 parents cf0d398 + 3d0a3c4 commit ac4b187
Show file tree
Hide file tree
Showing 52 changed files with 1,549 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public static String getOID(CodeSystem cs) {
return null;
}

private static ConceptDefinitionComponent findCode(List<ConceptDefinitionComponent> list, String code) {
public static ConceptDefinitionComponent findCode(List<ConceptDefinitionComponent> list, String code) {
for (ConceptDefinitionComponent c : list) {
if (c.getCode().equals(code))
return c;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public Element convert(Resource ig) throws IOException, FHIRException {
ByteArrayInputStream bi = new ByteArrayInputStream(bs.toByteArray());
List<NamedElement> list = new JsonParser(context).parse(bi);
if (list.size() != 1) {
throw new FHIRException("Unable to convert because the source contains multieple resources");
throw new FHIRException("Unable to convert because the source contains multiple resources");
}
return list.get(0).getElement();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2853,7 +2853,7 @@ else if (ToolingExtensions.hasExtension(expBase.getValueset().getExpansion(), To
} else {
boolean ok = true;
for (ValueSetExpansionContainsComponent cc : expDerived.getValueset().getExpansion().getContains()) {
ValidationResult vr = context.validateCode(null, cc.getSystem(), cc.getVersion(), cc.getCode(), null, baseVs);
ValidationResult vr = context.validateCode(new ValidationOptions(), cc.getSystem(), cc.getVersion(), cc.getCode(), null, baseVs);
if (!vr.isOk()) {
ok = false;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,10 @@ public ValueSetExpansionOutcome expandVS(ITerminologyOperationDetails opCtxt, Co

try {
ValueSet result = tc.getClient().expandValueset(vs, p);
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
if (res != null && res.getValueset() != null) {
res.getValueset().setUserData(UserDataNames.VS_EXPANSION_SOURCE, tc.getHost());
}
} catch (Exception e) {
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), TerminologyServiceErrorClass.UNKNOWN, true);
if (txLog != null) {
Expand Down Expand Up @@ -1012,7 +1015,10 @@ public ValueSetExpansionOutcome expandVS(String url, boolean cacheOk, boolean hi
throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET_2));
}
}
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
if (res != null && res.getValueset() != null) {
res.getValueset().setUserData(UserDataNames.VS_EXPANSION_SOURCE, tc.getHost());
}
} catch (Exception e) {
res = new ValueSetExpansionOutcome((e.getMessage() == null ? e.getClass().getName() : e.getMessage()), TerminologyServiceErrorClass.UNKNOWN, allErrors, true).setTxLink(txLog == null ? null : txLog.getLastId());
}
Expand Down Expand Up @@ -1085,6 +1091,9 @@ public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean h
res = null;
try {
res = vse.expand(vs, p);
if (res != null && res.getValueset() != null) {
res.getValueset().setUserData(UserDataNames.VS_EXPANSION_SOURCE, vse.getSource());
}
} catch (Exception e) {
allErrors.addAll(vse.getAllErrors());
e.printStackTrace();
Expand All @@ -1098,7 +1107,7 @@ public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean h
txCache.cacheExpansion(cacheToken, res, TerminologyCache.TRANSIENT);
return res;
}
if (res.getErrorClass() == TerminologyServiceErrorClass.INTERNAL_ERROR || isNoTerminologyServer()) { // this class is created specifically to say: don't consult the server
if (res.getErrorClass() == TerminologyServiceErrorClass.INTERNAL_ERROR || isNoTerminologyServer() || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNKNOWN) { // this class is created specifically to say: don't consult the server
return new ValueSetExpansionOutcome(res.getError(), res.getErrorClass(), false);
}

Expand Down Expand Up @@ -1133,6 +1142,9 @@ public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean h
res = new ValueSetExpansionOutcome((e.getMessage() == null ? e.getClass().getName() : e.getMessage()), TerminologyServiceErrorClass.UNKNOWN, allErrors, true).setTxLink(txLog == null ? null : txLog.getLastId());
}
}
if (res != null && res.getValueset() != null) {
res.getValueset().setUserData(UserDataNames.VS_EXPANSION_SOURCE, tc.getHost());
}
txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT);
return res;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public Element convert(Resource ig) throws IOException, FHIRException {
ByteArrayInputStream bi = new ByteArrayInputStream(bs.toByteArray());
List<ValidatedFragment> list = new JsonParser(context).parse(bi);
if (list.size() != 1) {
throw new FHIRException("Unable to convert because the source contains multieple resources");
throw new FHIRException("Unable to convert because the source contains multiple resources");
}
return list.get(0).getElement();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,20 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper
if (vs.hasCopyright())
generateCopyright(x, r);
}

if (vs.hasExtension(ToolingExtensions.EXT_VS_CS_SUPPL_NEEDED)) {
var p = x.para();
p.tx("This ValueSet requires the Code system Supplement ");
String u = ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_VS_CS_SUPPL_NEEDED);
CodeSystem cs = context.getContext().fetchResource(CodeSystem.class, u);
if (cs == null) {
p.code().tx(u);
} else if (!cs.hasWebPath()) {
p.ah(u).tx(cs.present());
} else {
p.ah(cs.getWebPath()).tx(cs.present());
}
p.tx(".");
}
if (vs.hasExpansion()) {
// for now, we just accept an expansion if there is one
generateExpansion(status, r, x, vs, false, maps);
Expand Down Expand Up @@ -498,14 +511,26 @@ private void generateVersionNotice(XhtmlNode x, ValueSetExpansionComponent expan
if (versions.size() == 1 && versions.get(s).size() == 1) {
for (String v : versions.get(s)) { // though there'll only be one
XhtmlNode p = x.para().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
p.tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSION)+" ");
if (!vs.hasUserData(UserDataNames.VS_EXPANSION_SOURCE)) {
p.tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSION)+" ");
} else if ("internal".equals(vs.getUserString(UserDataNames.VS_EXPANSION_SOURCE))) {
p.tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSION_INTERNAL)+" ");
} else {
p.tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSION_SRVR, vs.getUserString(UserDataNames.VS_EXPANSION_SOURCE))+" ");
}
expRef(p, s, v, vs);
}
} else {
for (String v : versions.get(s)) {
if (first) {
div = x.div().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
div.para().tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSIONS));
if (!vs.hasUserData(UserDataNames.VS_EXPANSION_SOURCE)) {
div.para().tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSIONS));
} else if ("internal".equals(vs.getUserString(UserDataNames.VS_EXPANSION_SOURCE))) {
div.para().tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSIONS_INTERNAL));
} else {
div.para().tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSIONS_SRVR, vs.getUserString(UserDataNames.VS_EXPANSION_SOURCE)));
}
ul = div.ul();
first = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS
import org.hl7.fhir.r5.model.IntegerType;
import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.Meta;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
Expand All @@ -65,6 +67,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptStatus;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedValueSet;
import org.hl7.fhir.r5.utils.CanonicalResourceUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.UserDataNames;
Expand Down Expand Up @@ -501,4 +504,97 @@ private static void addCodes(Set<String> res, ConceptSetComponent inc, List<Conc
}
}
}



public static String versionFromExpansionParams(Parameters expParameters, String system, String defaultVersion) {
for (ParametersParameterComponent p : expParameters.getParameter()) {
if ("system-version".equals(p.getName()) || "force-system-version".equals(p.getName())) {
String v = p.getValue().primitiveValue();
if (v.startsWith(system+"|")) {
String ver = v.substring(v.indexOf("|")+1);
if (defaultVersion == null || ver.startsWith(defaultVersion) || "force-system-version".equals(p.getName())) {
return ver;
}
}
}
}
return defaultVersion;
}

public static boolean isImplicitLoincValueSet(String url) {
return url.startsWith("http://loinc.org/vs");
}

public static boolean isImplicitSCTValueSet(String url) {
return url.startsWith("http://snomed.info/sct") && url.contains("?fhir_vs");
}

public static ValueSet makeImplicitValueSet(String url, String version) {
if (url.startsWith("http://snomed.info/sct")) {
return makeImplicitSCTVS(url, version);
} else if (url.startsWith("http://loinc.org/vs")) {
return makeImplicitLoincVS(url, version);
} else {
throw new FHIRException("Unknown implicit value set URL "+url);
}
}

private static ValueSet makeImplicitSCTVS(String url, String version) {
String query = url.substring(url.indexOf("?")+1);
if ("fhir_vs".equals(query)) {
ValueSet vs = new ValueSet();
vs.setUrl(url);
vs.setVersion(version);
vs.getCompose().addInclude().setSystem("http://snomed.info/sct");
return vs;
} else if (query.startsWith("fhir_vs=isa/")) {
ValueSet vs = new ValueSet();
vs.setUrl(url);
vs.setVersion(version);
vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue(query.substring(12));
return vs;
} else if (query.equals("fhir_vs=refset")) {
ValueSet vs = new ValueSet();
vs.setUrl(url);
vs.setVersion(version);
vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("refset-base");
return vs;
} else if (query.startsWith("fhir_vs=refset/")) {
ValueSet vs = new ValueSet();
vs.setUrl(url);
vs.setVersion(version);
vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.IN).setValue(query.substring(15));
return vs;
} else {
throw new FHIRException("Unknown implicit SNOMED CT value set URL "+url);
}
}

private static ValueSet makeImplicitLoincVS(String url, String version) {
if (url.equals("http://loinc.org/vs")) {
ValueSet vs = new ValueSet();
vs.setUrl(url);
vs.setVersion(version);
vs.getCompose().addInclude().setSystem("http://loinc.org");
return vs;
} else if (url.startsWith("http://loinc.org/vs/LP")) {
ValueSet vs = new ValueSet();
vs.setUrl(url);
vs.setVersion(version);
vs.getCompose().addInclude().setSystem("http://loinc.org").addFilter().setProperty("ancestor").setOp(FilterOperator.EQUAL).setValue(url.substring(21));
return vs;
} else if (url.startsWith("http://loinc.org/vs/LL")) {
ValueSet vs = new ValueSet();
vs.setUrl(url);
vs.setVersion(version);
// this isn't the actual definition, but it won't matter to us internally
vs.getCompose().addInclude().setSystem("http://loinc.org").addFilter().setProperty("answer-list").setOp(FilterOperator.EQUAL).setValue(url.substring(21));
return vs;
} else {
throw new FHIRException("Unknown implicit LOINC value set URL "+url);
}
}


}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.hl7.fhir.r5.terminologies.client;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
Expand Down Expand Up @@ -218,5 +220,14 @@ public static boolean isCanUseCacheId() {
public static void setCanUseCacheId(boolean canUseCacheId) {
TerminologyClientContext.canUseCacheId = canUseCacheId;
}

public String getHost() {
try {
URL uri = new URL(getAddress());
return uri.getHost();
} catch (MalformedURLException e) {
return getAddress();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
Expand Down Expand Up @@ -562,7 +563,28 @@ public SourcedValueSet findValueSetOnServer(String canonical) {
if (IGNORE_TX_REGISTRY || getMasterClient() == null) {
return null;
}
String request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&valueSet="+Utilities.URLEncode(canonical));
String request = null;
boolean isImplicit = false;
String iVersion = null;
if (ValueSetUtilities.isImplicitSCTValueSet(canonical)) {
isImplicit = true;
iVersion = canonical.substring(0, canonical.indexOf("?fhir_vs"));
if ("http://snomed.info/sct".equals(iVersion) && canonical.contains("|")) {
iVersion = canonical.substring(canonical.indexOf("|")+1);
}
iVersion = ValueSetUtilities.versionFromExpansionParams(expParameters, "http://snomed.info/sct", iVersion);
request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&url="+Utilities.URLEncode("http://snomed.info/sct"+(iVersion == null ? "": "|"+iVersion)));
} else if (ValueSetUtilities.isImplicitLoincValueSet(canonical)) {
isImplicit = true;
iVersion = null;
if (canonical.contains("|")) {
iVersion = canonical.substring(canonical.indexOf("|")+1);
}
iVersion = ValueSetUtilities.versionFromExpansionParams(expParameters, "http://loinc.org", iVersion);
request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&url="+Utilities.URLEncode("http://loinc.org"+(iVersion == null ? "": "|"+iVersion)));
} else {
request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&valueSet="+Utilities.URLEncode(canonical));
}
String server = null;
try {
if (!useEcosystem) {
Expand Down Expand Up @@ -628,7 +650,23 @@ public SourcedValueSet findValueSetOnServer(String canonical) {
Bundle bnd = client.getClient().search("ValueSet", criteria);
String rid = null;
if (bnd.getEntry().size() == 0) {
return null;
if (isImplicit) {
// couldn't find it, but can we expand on it?
Parameters p= new Parameters();
p.addParameter("url", new UriType(canonical));
p.addParameter("count", 0);
p.addParameters(expParameters);
try {
ValueSet vs = client.getClient().expandValueset(null, p);
if (vs != null) {
return new SourcedValueSet(server, ValueSetUtilities.makeImplicitValueSet(canonical, iVersion));
}
} catch (Exception e) {
return null;
}
} else {
return null;
}
} else if (bnd.getEntry().size() > 1) {
List<ValueSet> vslist = new ArrayList<>();
for (BundleEntryComponent be : bnd.getEntry()) {
Expand Down Expand Up @@ -659,7 +697,6 @@ public SourcedValueSet findValueSetOnServer(String canonical) {
return null;
}
}

public SourcedCodeSystem findCodeSystemOnServer(String canonical) {
if (IGNORE_TX_REGISTRY || getMasterClient() == null || !useEcosystem) {
return null;
Expand Down
Loading

0 comments on commit ac4b187

Please sign in to comment.