Add support for subsumes in tx client
This commit is contained in:
parent
981d201046
commit
df3456d293
|
@ -117,6 +117,13 @@ public class TerminologyClientR2 implements ITerminologyClient {
|
||||||
p2 = client.operateType(org.hl7.fhir.dstu2.model.ValueSet.class, "validate-code", p2);
|
p2 = client.operateType(org.hl7.fhir.dstu2.model.ValueSet.class, "validate-code", p2);
|
||||||
return (Parameters) VersionConvertorFactory_10_50.convertResource(p2);
|
return (Parameters) VersionConvertorFactory_10_50.convertResource(p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parameters subsumes(Parameters pin) throws FHIRException {
|
||||||
|
org.hl7.fhir.dstu2.model.Parameters p2 = (org.hl7.fhir.dstu2.model.Parameters) VersionConvertorFactory_10_50.convertResource(pin);
|
||||||
|
p2 = client.operateType(org.hl7.fhir.dstu2.model.ValueSet.class, "subsumes", p2);
|
||||||
|
return (Parameters) VersionConvertorFactory_10_50.convertResource(p2);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Parameters validateVS(Parameters pin) throws FHIRException {
|
public Parameters validateVS(Parameters pin) throws FHIRException {
|
||||||
|
@ -238,4 +245,5 @@ public class TerminologyClientR2 implements ITerminologyClient {
|
||||||
org.hl7.fhir.dstu2.model.Bundle result = client.search(type, criteria);
|
org.hl7.fhir.dstu2.model.Bundle result = client.search(type, criteria);
|
||||||
return result == null ? null : (Bundle) VersionConvertorFactory_10_50.convertResource(result);
|
return result == null ? null : (Bundle) VersionConvertorFactory_10_50.convertResource(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -242,4 +242,11 @@ public class TerminologyClientR3 implements ITerminologyClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parameters subsumes(Parameters pin) throws FHIRException {
|
||||||
|
org.hl7.fhir.dstu3.model.Parameters p2 = (org.hl7.fhir.dstu3.model.Parameters) VersionConvertorFactory_30_50.convertResource(pin);
|
||||||
|
p2 = client.operateType(org.hl7.fhir.dstu3.model.CodeSystem.class, "subsumes", p2);
|
||||||
|
return (Parameters) VersionConvertorFactory_30_50.convertResource(p2);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -111,6 +111,25 @@ public class TerminologyClientR4 implements ITerminologyClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parameters subsumes(Parameters pin) throws FHIRException {
|
||||||
|
try {
|
||||||
|
org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) VersionConvertorFactory_40_50.convertResource(pin);
|
||||||
|
p2 = client.operateType(org.hl7.fhir.r4.model.CodeSystem.class, "subsumes", p2);
|
||||||
|
return (Parameters) VersionConvertorFactory_40_50.convertResource(p2);
|
||||||
|
} catch (EFhirClientException e) {
|
||||||
|
if (e.getServerErrors().size() == 1) {
|
||||||
|
OperationOutcome op = (OperationOutcome) VersionConvertorFactory_40_50.convertResource(e.getServerErrors().get(0));
|
||||||
|
throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getMessage(), op, e);
|
||||||
|
} else {
|
||||||
|
throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new FHIRException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Parameters validateVS(Parameters pin) throws FHIRException {
|
public Parameters validateVS(Parameters pin) throws FHIRException {
|
||||||
try {
|
try {
|
||||||
|
@ -249,6 +268,5 @@ public class TerminologyClientR4 implements ITerminologyClient {
|
||||||
org.hl7.fhir.r4.model.Bundle result = client.search(type, criteria);
|
org.hl7.fhir.r4.model.Bundle result = client.search(type, criteria);
|
||||||
return result == null ? null : (Bundle) VersionConvertorFactory_40_50.convertResource(result);
|
return result == null ? null : (Bundle) VersionConvertorFactory_40_50.convertResource(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1347,7 +1347,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
Set<String> systems = findRelevantSystems(code, vs);
|
Set<String> systems = findRelevantSystems(code, vs);
|
||||||
TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false);
|
TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false);
|
||||||
|
|
||||||
String csumm =cachingAllowed && txCache != null ? txCache.summary(code) : null;
|
String csumm = cachingAllowed && txCache != null ? txCache.summary(code) : null;
|
||||||
if (cachingAllowed && txCache != null) {
|
if (cachingAllowed && txCache != null) {
|
||||||
txLog("$validate "+csumm+(vs == null ? "" : " for "+ txCache.summary(vs))+" on "+tc.getAddress());
|
txLog("$validate "+csumm+(vs == null ? "" : " for "+ txCache.summary(vs))+" on "+tc.getAddress());
|
||||||
} else {
|
} else {
|
||||||
|
@ -1377,6 +1377,83 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ask the terminology system whether parent subsumes child.
|
||||||
|
*
|
||||||
|
* @return true if it does, false if it doesn't, and null if it's not know whether it does
|
||||||
|
*/
|
||||||
|
public Boolean subsumes(ValidationOptions optionsArg, Coding parent, Coding child) {
|
||||||
|
ValidationOptions options = optionsArg != null ? optionsArg : ValidationOptions.defaults();
|
||||||
|
|
||||||
|
if (parent.hasSystem()) {
|
||||||
|
codeSystemsUsed.add(parent.getSystem());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (child.hasSystem()) {
|
||||||
|
codeSystemsUsed.add(child.getSystem());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CacheToken cacheToken = cachingAllowed && txCache != null ? txCache.generateSubsumesToken(options, parent, child, expParameters) : null;
|
||||||
|
if (cachingAllowed && txCache != null) {
|
||||||
|
Boolean res = txCache.getSubsumes(cacheToken);
|
||||||
|
if (res != null) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.isUseClient() && parent.getSystem().equals(child.getSystem())) {
|
||||||
|
CodeSystem cs = fetchCodeSystem(parent.getSystem());
|
||||||
|
if (cs != null) {
|
||||||
|
Boolean b = CodeSystemUtilities.subsumes(cs, parent.getCode(), child.getCode());
|
||||||
|
if (txCache != null && cachingAllowed) {
|
||||||
|
txCache.cacheSubsumes(cacheToken, b, true);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!terminologyClientManager.hasClient() || !options.isUseServer() || unsupportedCodeSystems.contains(parent.getSystem()) || unsupportedCodeSystems.contains(child.getSystem()) || noTerminologyServer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> systems = new HashSet<>();
|
||||||
|
systems.add(parent.getSystem());
|
||||||
|
systems.add(child.getSystem());
|
||||||
|
TerminologyClientContext tc = terminologyClientManager.chooseServer(null, systems, false);
|
||||||
|
|
||||||
|
txLog("$subsumes "+parent.toString()+" > "+child.toString()+" on "+tc.getAddress());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Parameters pIn = new Parameters();
|
||||||
|
pIn.addParameter().setName("codingA").setValue(parent);
|
||||||
|
pIn.addParameter().setName("codingB").setValue(child);
|
||||||
|
if (txLog != null) {
|
||||||
|
txLog.clearLastId();
|
||||||
|
}
|
||||||
|
Parameters pOut = tc.getClient().subsumes(pIn);
|
||||||
|
return processSubsumesResult(pOut, tc.getClient().getAddress());
|
||||||
|
} catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Boolean processSubsumesResult(Parameters pOut, String server) {
|
||||||
|
for (ParametersParameterComponent p : pOut.getParameter()) {
|
||||||
|
if (p.hasValue()) {
|
||||||
|
if (p.getName().equals("outcome")) {
|
||||||
|
return Utilities.existsInList(p.getValue().primitiveValue(), "equivalent", "subsumes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected ValueSetExpander constructValueSetExpanderSimple(ValidationOptions options) {
|
protected ValueSetExpander constructValueSetExpanderSimple(ValidationOptions options) {
|
||||||
return new ValueSetExpander(this, new TerminologyOperationContext(this, options)).setDebug(logger.isDebugLogging());
|
return new ValueSetExpander(this, new TerminologyOperationContext(this, options)).setDebug(logger.isDebugLogging());
|
||||||
}
|
}
|
||||||
|
|
|
@ -640,4 +640,11 @@ public interface IWorkerContext {
|
||||||
public <T extends Resource> T findTxResource(Class<T> class_, String canonical);
|
public <T extends Resource> T findTxResource(Class<T> class_, String canonical);
|
||||||
public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version);
|
public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ask the terminology system whether parent subsumes child.
|
||||||
|
*
|
||||||
|
* @return true if it does, false if it doesn't, and null if it's not know whether it does
|
||||||
|
*/
|
||||||
|
public Boolean subsumes(ValidationOptions options, Coding parent, Coding child);
|
||||||
|
|
||||||
}
|
}
|
|
@ -54,6 +54,7 @@ public interface ITerminologyClient {
|
||||||
ValueSet expandValueset(ValueSet vs, Parameters p) throws FHIRException;
|
ValueSet expandValueset(ValueSet vs, Parameters p) throws FHIRException;
|
||||||
Parameters validateCS(Parameters pin) throws FHIRException;
|
Parameters validateCS(Parameters pin) throws FHIRException;
|
||||||
Parameters validateVS(Parameters pin) throws FHIRException;
|
Parameters validateVS(Parameters pin) throws FHIRException;
|
||||||
|
Parameters subsumes(Parameters pin) throws FHIRException;
|
||||||
ITerminologyClient setTimeoutFactor(int i) throws FHIRException;
|
ITerminologyClient setTimeoutFactor(int i) throws FHIRException;
|
||||||
ToolingClientLogger getLogger();
|
ToolingClientLogger getLogger();
|
||||||
ITerminologyClient setLogger(ToolingClientLogger txLog) throws FHIRException;
|
ITerminologyClient setLogger(ToolingClientLogger txLog) throws FHIRException;
|
||||||
|
|
|
@ -135,6 +135,11 @@ public class TerminologyClientR5 implements ITerminologyClient {
|
||||||
return client.operateType(CodeSystem.class, "validate-code", pin);
|
return client.operateType(CodeSystem.class, "validate-code", pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parameters subsumes(Parameters pin) {
|
||||||
|
return client.operateType(CodeSystem.class, "subsumes", pin);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Parameters validateVS(Parameters pin) {
|
public Parameters validateVS(Parameters pin) {
|
||||||
return client.operateType(ValueSet.class, "validate-code", pin);
|
return client.operateType(ValueSet.class, "validate-code", pin);
|
||||||
|
@ -256,4 +261,5 @@ public class TerminologyClientR5 implements ITerminologyClient {
|
||||||
return client.search(type, criteria);
|
return client.search(type, criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -148,6 +148,21 @@ public class TerminologyCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SubsumesResult {
|
||||||
|
|
||||||
|
private Boolean result;
|
||||||
|
|
||||||
|
protected SubsumesResult(Boolean result) {
|
||||||
|
super();
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected SystemNameKeyGenerator getSystemNameKeyGenerator() {
|
protected SystemNameKeyGenerator getSystemNameKeyGenerator() {
|
||||||
return systemNameKeyGenerator;
|
return systemNameKeyGenerator;
|
||||||
}
|
}
|
||||||
|
@ -218,6 +233,7 @@ public class TerminologyCache {
|
||||||
private boolean persistent;
|
private boolean persistent;
|
||||||
private ValidationResult v;
|
private ValidationResult v;
|
||||||
private ValueSetExpansionOutcome e;
|
private ValueSetExpansionOutcome e;
|
||||||
|
private SubsumesResult s;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NamedCache {
|
private class NamedCache {
|
||||||
|
@ -381,9 +397,9 @@ public class TerminologyCache {
|
||||||
if (code.hasSystem()) {
|
if (code.hasSystem()) {
|
||||||
ct.setName(code.getSystem());
|
ct.setName(code.getSystem());
|
||||||
ct.hasVersion = code.hasVersion();
|
ct.hasVersion = code.hasVersion();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
ct.name = NAME_FOR_NO_SYSTEM;
|
ct.name = NAME_FOR_NO_SYSTEM;
|
||||||
|
}
|
||||||
ct.setName(vsUrl);
|
ct.setName(vsUrl);
|
||||||
JsonParser json = new JsonParser();
|
JsonParser json = new JsonParser();
|
||||||
json.setOutputStyle(OutputStyle.PRETTY);
|
json.setOutputStyle(OutputStyle.PRETTY);
|
||||||
|
@ -633,6 +649,9 @@ public class TerminologyCache {
|
||||||
if (ce.e.getValueset() != null)
|
if (ce.e.getValueset() != null)
|
||||||
sw.write(" \"valueSet\" : "+json.composeString(ce.e.getValueset()).trim()+",\r\n");
|
sw.write(" \"valueSet\" : "+json.composeString(ce.e.getValueset()).trim()+",\r\n");
|
||||||
sw.write(" \"error\" : \""+Utilities.escapeJson(ce.e.getError()).trim()+"\"\r\n}\r\n");
|
sw.write(" \"error\" : \""+Utilities.escapeJson(ce.e.getError()).trim()+"\"\r\n}\r\n");
|
||||||
|
} else if (ce.s != null) {
|
||||||
|
sw.write("s: {\r\n");
|
||||||
|
sw.write(" \"result\" : "+ce.s.result+"\r\n}\r\n");
|
||||||
} else {
|
} else {
|
||||||
sw.write("v: {\r\n");
|
sw.write("v: {\r\n");
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
|
@ -743,15 +762,17 @@ public class TerminologyCache {
|
||||||
CacheEntry ce = new CacheEntry();
|
CacheEntry ce = new CacheEntry();
|
||||||
ce.persistent = true;
|
ce.persistent = true;
|
||||||
ce.request = request;
|
ce.request = request;
|
||||||
boolean e = resultString.charAt(0) == 'e';
|
char e = resultString.charAt(0);
|
||||||
resultString = resultString.substring(3);
|
resultString = resultString.substring(3);
|
||||||
JsonObject o = (JsonObject) new com.google.gson.JsonParser().parse(resultString);
|
JsonObject o = (JsonObject) new com.google.gson.JsonParser().parse(resultString);
|
||||||
String error = loadJS(o.get("error"));
|
String error = loadJS(o.get("error"));
|
||||||
if (e) {
|
if (e == 'e') {
|
||||||
if (o.has("valueSet"))
|
if (o.has("valueSet"))
|
||||||
ce.e = new ValueSetExpansionOutcome((ValueSet) new JsonParser().parse(o.getAsJsonObject("valueSet")), error, TerminologyServiceErrorClass.UNKNOWN, o.has("from-server"));
|
ce.e = new ValueSetExpansionOutcome((ValueSet) new JsonParser().parse(o.getAsJsonObject("valueSet")), error, TerminologyServiceErrorClass.UNKNOWN, o.has("from-server"));
|
||||||
else
|
else
|
||||||
ce.e = new ValueSetExpansionOutcome(error, TerminologyServiceErrorClass.UNKNOWN, o.has("from-server"));
|
ce.e = new ValueSetExpansionOutcome(error, TerminologyServiceErrorClass.UNKNOWN, o.has("from-server"));
|
||||||
|
} else if (e == 's') {
|
||||||
|
ce.s = new SubsumesResult(o.get("result").getAsBoolean());
|
||||||
} else {
|
} else {
|
||||||
String t = loadJS(o.get("severity"));
|
String t = loadJS(o.get("severity"));
|
||||||
IssueSeverity severity = t == null ? null : IssueSeverity.fromCode(t);
|
IssueSeverity severity = t == null ? null : IssueSeverity.fromCode(t);
|
||||||
|
@ -934,15 +955,15 @@ public class TerminologyCache {
|
||||||
|
|
||||||
public Map<String, String> servers() {
|
public Map<String, String> servers() {
|
||||||
Map<String, String> servers = new HashMap<>();
|
Map<String, String> servers = new HashMap<>();
|
||||||
servers.put("http://local.fhir.org/r2", "tx.fhir.org");
|
// servers.put("http://local.fhir.org/r2", "tx.fhir.org");
|
||||||
servers.put("http://local.fhir.org/r3", "tx.fhir.org");
|
// servers.put("http://local.fhir.org/r3", "tx.fhir.org");
|
||||||
servers.put("http://local.fhir.org/r4", "tx.fhir.org");
|
// servers.put("http://local.fhir.org/r4", "tx.fhir.org");
|
||||||
servers.put("http://local.fhir.org/r5", "tx.fhir.org");
|
// servers.put("http://local.fhir.org/r5", "tx.fhir.org");
|
||||||
|
//
|
||||||
servers.put("http://tx-dev.fhir.org/r2", "tx.fhir.org");
|
// servers.put("http://tx-dev.fhir.org/r2", "tx.fhir.org");
|
||||||
servers.put("http://tx-dev.fhir.org/r3", "tx.fhir.org");
|
// servers.put("http://tx-dev.fhir.org/r3", "tx.fhir.org");
|
||||||
servers.put("http://tx-dev.fhir.org/r4", "tx.fhir.org");
|
// servers.put("http://tx-dev.fhir.org/r4", "tx.fhir.org");
|
||||||
servers.put("http://tx-dev.fhir.org/r5", "tx.fhir.org");
|
// servers.put("http://tx-dev.fhir.org/r5", "tx.fhir.org");
|
||||||
|
|
||||||
servers.put("http://tx.fhir.org/r2", "tx.fhir.org");
|
servers.put("http://tx.fhir.org/r2", "tx.fhir.org");
|
||||||
servers.put("http://tx.fhir.org/r3", "tx.fhir.org");
|
servers.put("http://tx.fhir.org/r3", "tx.fhir.org");
|
||||||
|
@ -1002,5 +1023,58 @@ public class TerminologyCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CacheToken generateSubsumesToken(ValidationOptions options, Coding parent, Coding child, Parameters expParameters) {
|
||||||
|
try {
|
||||||
|
CacheToken ct = new CacheToken();
|
||||||
|
if (parent.hasSystem()) {
|
||||||
|
ct.setName(parent.getSystem());
|
||||||
|
}
|
||||||
|
if (child.hasSystem()) {
|
||||||
|
ct.setName(child.getSystem());
|
||||||
|
}
|
||||||
|
ct.hasVersion = parent.hasVersion() || child.hasVersion();
|
||||||
|
JsonParser json = new JsonParser();
|
||||||
|
json.setOutputStyle(OutputStyle.PRETTY);
|
||||||
|
String expJS = json.composeString(expParameters);
|
||||||
|
ct.request = "{\"op\": \"subsumes\", \"parent\" : "+json.composeString(parent, "code")+", \"child\" :"+json.composeString(child, "code")+(options == null ? "" : ", "+options.toJson())+", \"profile\": "+expJS+"}";
|
||||||
|
ct.key = String.valueOf(hashJson(ct.request));
|
||||||
|
return ct;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getSubsumes(CacheToken cacheToken) {
|
||||||
|
if (cacheToken.key == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
synchronized (lock) {
|
||||||
|
requestCount++;
|
||||||
|
NamedCache nc = getNamedCache(cacheToken);
|
||||||
|
CacheEntry e = nc.map.get(cacheToken.key);
|
||||||
|
if (e == null) {
|
||||||
|
networkCount++;
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
hitCount++;
|
||||||
|
return e.s.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cacheSubsumes(CacheToken cacheToken, Boolean b, boolean persistent) {
|
||||||
|
if (cacheToken.key != null) {
|
||||||
|
synchronized (lock) {
|
||||||
|
NamedCache nc = getNamedCache(cacheToken);
|
||||||
|
CacheEntry e = new CacheEntry();
|
||||||
|
e.request = cacheToken.request;
|
||||||
|
e.persistent = persistent;
|
||||||
|
e.s = new SubsumesResult(b);
|
||||||
|
store(cacheToken, persistent, nc, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue