fix -ips parameter and add -check-ips-codes parameter
This commit is contained in:
parent
52577a861c
commit
fefd1ad1d6
|
@ -1017,6 +1017,106 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateCodeBatchByRef(ValidationOptions options, List<? extends CodingValidationRequest> codes, String vsUrl) {
|
||||||
|
if (options == null) {
|
||||||
|
options = ValidationOptions.defaults();
|
||||||
|
}
|
||||||
|
// 1st pass: what is in the cache?
|
||||||
|
// 2nd pass: What can we do internally
|
||||||
|
// 3rd pass: hit the server
|
||||||
|
for (CodingValidationRequest t : codes) {
|
||||||
|
t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vsUrl, expParameters) : null);
|
||||||
|
if (t.getCoding().hasSystem()) {
|
||||||
|
codeSystemsUsed.add(t.getCoding().getSystem());
|
||||||
|
}
|
||||||
|
if (txCache != null) {
|
||||||
|
t.setResult(txCache.getValidation(t.getCacheToken()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options.isUseClient()) {
|
||||||
|
ValueSet vs = fetchResource(ValueSet.class, vsUrl);
|
||||||
|
if (vs != null) {
|
||||||
|
for (CodingValidationRequest t : codes) {
|
||||||
|
if (!t.hasResult()) {
|
||||||
|
try {
|
||||||
|
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
|
||||||
|
vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null);
|
||||||
|
ValidationResult res = vsc.validateCode("Coding", t.getCoding());
|
||||||
|
if (txCache != null) {
|
||||||
|
txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT);
|
||||||
|
}
|
||||||
|
t.setResult(res);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CodingValidationRequest t : codes) {
|
||||||
|
if (!t.hasResult()) {
|
||||||
|
String codeKey = t.getCoding().hasVersion() ? t.getCoding().getSystem()+"|"+t.getCoding().getVersion() : t.getCoding().getSystem();
|
||||||
|
if (!options.isUseServer()) {
|
||||||
|
t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null));
|
||||||
|
} else if (unsupportedCodeSystems.contains(codeKey)) {
|
||||||
|
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null));
|
||||||
|
} else if (noTerminologyServer) {
|
||||||
|
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expParameters == null)
|
||||||
|
throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED));
|
||||||
|
// for those that that failed, we try to validate on the server
|
||||||
|
Bundle batch = new Bundle();
|
||||||
|
batch.setType(BundleType.BATCH);
|
||||||
|
Set<String> systems = new HashSet<>();
|
||||||
|
for (CodingValidationRequest codingValidationRequest : codes) {
|
||||||
|
if (!codingValidationRequest.hasResult()) {
|
||||||
|
Parameters pIn = constructParameters(options, codingValidationRequest, vsUrl);
|
||||||
|
setTerminologyOptions(options, pIn);
|
||||||
|
BundleEntryComponent be = batch.addEntry();
|
||||||
|
be.setResource(pIn);
|
||||||
|
be.getRequest().setMethod(HTTPVerb.POST);
|
||||||
|
if (vsUrl != null) {
|
||||||
|
be.getRequest().setUrl("ValueSet/$validate-code");
|
||||||
|
} else {
|
||||||
|
be.getRequest().setUrl("CodeSystem/$validate-code");
|
||||||
|
}
|
||||||
|
be.setUserData("source", codingValidationRequest);
|
||||||
|
systems.add(codingValidationRequest.getCoding().getSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (batch.getEntry().size() > 0) {
|
||||||
|
txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString());
|
||||||
|
if (tcc.getClient() == null) {
|
||||||
|
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
|
||||||
|
}
|
||||||
|
if (txLog != null) {
|
||||||
|
txLog.clearLastId();
|
||||||
|
}
|
||||||
|
Bundle resp = tcc.getClient().validateBatch(batch);
|
||||||
|
if (resp == null) {
|
||||||
|
throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < batch.getEntry().size(); i++) {
|
||||||
|
CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source");
|
||||||
|
BundleEntryComponent r = resp.getEntry().get(i);
|
||||||
|
|
||||||
|
if (r.getResource() instanceof Parameters) {
|
||||||
|
t.setResult(processValidationResult((Parameters) r.getResource()));
|
||||||
|
if (txCache != null) {
|
||||||
|
txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource()), null).setTxLink(txLog == null ? null : txLog.getLastId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getResponseText(Resource resource) {
|
private String getResponseText(Resource resource) {
|
||||||
if (resource instanceof OperationOutcome) {
|
if (resource instanceof OperationOutcome) {
|
||||||
return OperationOutcomeRenderer.toString((OperationOutcome) resource);
|
return OperationOutcomeRenderer.toString((OperationOutcome) resource);
|
||||||
|
@ -1197,6 +1297,19 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
return pIn;
|
return pIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Parameters constructParameters(ValidationOptions options, CodingValidationRequest codingValidationRequest, String vsUrl) {
|
||||||
|
Parameters pIn = new Parameters();
|
||||||
|
pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding());
|
||||||
|
if (options.isGuessSystem()) {
|
||||||
|
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
|
||||||
|
}
|
||||||
|
if (vsUrl != null) {
|
||||||
|
pIn.addParameter().setName("url").setValue(new CanonicalType(vsUrl));
|
||||||
|
}
|
||||||
|
pIn.addParameter().setName("profile").setResource(expParameters);
|
||||||
|
return pIn;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateUnsupportedCodeSystems(ValidationResult res, Coding code, String codeKey) {
|
private void updateUnsupportedCodeSystems(ValidationResult res, Coding code, String codeKey) {
|
||||||
if (res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && !code.hasVersion()) {
|
if (res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && !code.hasVersion()) {
|
||||||
unsupportedCodeSystems.add(codeKey);
|
unsupportedCodeSystems.add(codeKey);
|
||||||
|
|
|
@ -321,12 +321,23 @@ public interface IWorkerContext {
|
||||||
private Coding coding;
|
private Coding coding;
|
||||||
private ValidationResult result;
|
private ValidationResult result;
|
||||||
private CacheToken cacheToken;
|
private CacheToken cacheToken;
|
||||||
|
private String vs;
|
||||||
|
|
||||||
public CodingValidationRequest(Coding coding) {
|
public CodingValidationRequest(Coding coding) {
|
||||||
super();
|
super();
|
||||||
this.coding = coding;
|
this.coding = coding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CodingValidationRequest(Coding coding, String vs) {
|
||||||
|
super();
|
||||||
|
this.coding = coding;
|
||||||
|
this.vs = vs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVs() {
|
||||||
|
return vs;
|
||||||
|
}
|
||||||
|
|
||||||
public ValidationResult getResult() {
|
public ValidationResult getResult() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -826,6 +837,7 @@ public interface IWorkerContext {
|
||||||
* @param vs
|
* @param vs
|
||||||
*/
|
*/
|
||||||
public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs);
|
public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs);
|
||||||
|
public void validateCodeBatchByRef(ValidationOptions options, List<? extends CodingValidationRequest> codes, String vsUrl);
|
||||||
|
|
||||||
|
|
||||||
// todo: figure these out
|
// todo: figure these out
|
||||||
|
|
|
@ -278,6 +278,28 @@ public class TerminologyCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CacheToken generateValidationToken(ValidationOptions options, Coding code, String vsUrl, Parameters expParameters) {
|
||||||
|
try {
|
||||||
|
CacheToken ct = new CacheToken();
|
||||||
|
if (code.hasSystem()) {
|
||||||
|
ct.setName(code.getSystem());
|
||||||
|
ct.hasVersion = code.hasVersion();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ct.name = NAME_FOR_NO_SYSTEM;
|
||||||
|
ct.setName(vsUrl);
|
||||||
|
JsonParser json = new JsonParser();
|
||||||
|
json.setOutputStyle(OutputStyle.PRETTY);
|
||||||
|
String expJS = json.composeString(expParameters);
|
||||||
|
|
||||||
|
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsUrl == null ? "null" : vsUrl)+(options == null ? "" : ", "+options.toJson())+", \"profile\": "+expJS+"}";
|
||||||
|
ct.key = String.valueOf(hashJson(ct.request));
|
||||||
|
return ct;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String extracted(JsonParser json, ValueSet vsc) throws IOException {
|
public String extracted(JsonParser json, ValueSet vsc) throws IOException {
|
||||||
String s = null;
|
String s = null;
|
||||||
if (vsc.getExpansion().getContains().size() > 1000 || vsc.getCompose().getIncludeFirstRep().getConcept().size() > 1000) {
|
if (vsc.getExpansion().getContains().size() > 1000 || vsc.getCompose().getIncludeFirstRep().getConcept().size() > 1000) {
|
||||||
|
|
|
@ -225,6 +225,7 @@ public class I18nConstants {
|
||||||
public static final String MEASURE_MR_GRP_POP_NO_COUNT = "MEASURE_MR_GRP_POP_NO_COUNT";
|
public static final String MEASURE_MR_GRP_POP_NO_COUNT = "MEASURE_MR_GRP_POP_NO_COUNT";
|
||||||
public static final String MEASURE_MR_GRP_POP_NO_SUBJECTS = "MEASURE_MR_GRP_POP_NO_SUBJECTS";
|
public static final String MEASURE_MR_GRP_POP_NO_SUBJECTS = "MEASURE_MR_GRP_POP_NO_SUBJECTS";
|
||||||
public static final String MEASURE_MR_GRP_POP_UNK_CODE = "MEASURE_MR_GRP_POP_UNK_CODE";
|
public static final String MEASURE_MR_GRP_POP_UNK_CODE = "MEASURE_MR_GRP_POP_UNK_CODE";
|
||||||
|
public static final String MEASURE_MR_GRPST_POP_UNK_CODE = "MEASURE_MR_GRPST_POP_UNK_CODE";
|
||||||
public static final String MEASURE_MR_GRP_UNK_CODE = "MEASURE_MR_GRP_UNK_CODE";
|
public static final String MEASURE_MR_GRP_UNK_CODE = "MEASURE_MR_GRP_UNK_CODE";
|
||||||
public static final String MEASURE_MR_M_NONE = "Measure_MR_M_None";
|
public static final String MEASURE_MR_M_NONE = "Measure_MR_M_None";
|
||||||
public static final String MEASURE_MR_M_NOTFOUND = "Measure_MR_M_NotFound";
|
public static final String MEASURE_MR_M_NOTFOUND = "Measure_MR_M_NotFound";
|
||||||
|
@ -924,6 +925,7 @@ public class I18nConstants {
|
||||||
public static final String SD_ED_TYPE_PROFILE_WRONG_TYPE = "SD_ED_TYPE_PROFILE_WRONG_TYPE";
|
public static final String SD_ED_TYPE_PROFILE_WRONG_TYPE = "SD_ED_TYPE_PROFILE_WRONG_TYPE";
|
||||||
public static final String VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = "VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED";
|
public static final String VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = "VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED";
|
||||||
public static final String VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = "VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED";
|
public static final String VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = "VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED";
|
||||||
|
public static final String CS_SCT_IPS_NOT_IPS = "CS_SCT_IPS_NOT_IPS";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -501,6 +501,7 @@ DUPLICATE_ID = Duplicate id value ''{0}''
|
||||||
TERMINOLOGY_TX_SYSTEM_NO_CODE = A code with no system has no defined meaning. A system should be provided
|
TERMINOLOGY_TX_SYSTEM_NO_CODE = A code with no system has no defined meaning. A system should be provided
|
||||||
MEASURE_MR_GRP_POP_NO_CODE = Group should have a code that matches the group population definition in the measure
|
MEASURE_MR_GRP_POP_NO_CODE = Group should have a code that matches the group population definition in the measure
|
||||||
MEASURE_MR_GRP_POP_UNK_CODE = The code for this group population has no match in the measure definition
|
MEASURE_MR_GRP_POP_UNK_CODE = The code for this group population has no match in the measure definition
|
||||||
|
MEASURE_MR_GRPST_POP_UNK_CODE = The code for this group stratifier has no match in the measure definition
|
||||||
MEASURE_MR_GRP_POP_DUPL_CODE = The code for this group population is duplicated with another group
|
MEASURE_MR_GRP_POP_DUPL_CODE = The code for this group population is duplicated with another group
|
||||||
MEASURE_MR_GRP_POP_MISSING_BY_CODE = The MeasureReport does not include a population group for the population group {0}
|
MEASURE_MR_GRP_POP_MISSING_BY_CODE = The MeasureReport does not include a population group for the population group {0}
|
||||||
MEASURE_MR_GRP_POP_COUNT_MISMATCH = Mismatch between count {0} and number of subjects {1}
|
MEASURE_MR_GRP_POP_COUNT_MISMATCH = Mismatch between count {0} and number of subjects {1}
|
||||||
|
@ -979,3 +980,4 @@ SD_ED_TYPE_WRONG_TYPE_one = The element has a type {0} which is different to the
|
||||||
SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the types {1} on the base profile {2}
|
SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the types {1} on the base profile {2}
|
||||||
VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = This include has some concepts with displays and some without - check that this is what is intended
|
VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = This include has some concepts with displays and some without - check that this is what is intended
|
||||||
VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = This SNOMED-CT based include has some concepts with semantic tags (FSN terms) and some without (preferred terms) - check that this is what is intended
|
VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = This SNOMED-CT based include has some concepts with semantic tags (FSN terms) and some without (preferred terms) - check that this is what is intended
|
||||||
|
CS_SCT_IPS_NOT_IPS = The Snomed CT code {0} ({1}) is not a member of the IPS free set
|
||||||
|
|
|
@ -212,6 +212,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
|
||||||
@Getter @Setter private boolean doImplicitFHIRPathStringConversion;
|
@Getter @Setter private boolean doImplicitFHIRPathStringConversion;
|
||||||
@Getter @Setter private HtmlInMarkdownCheck htmlInMarkdownCheck;
|
@Getter @Setter private HtmlInMarkdownCheck htmlInMarkdownCheck;
|
||||||
@Getter @Setter private boolean allowDoubleQuotesInFHIRPath;
|
@Getter @Setter private boolean allowDoubleQuotesInFHIRPath;
|
||||||
|
@Getter @Setter private boolean checkIPSCodes;
|
||||||
@Getter @Setter private Locale locale;
|
@Getter @Setter private Locale locale;
|
||||||
@Getter @Setter private List<ImplementationGuide> igs = new ArrayList<>();
|
@Getter @Setter private List<ImplementationGuide> igs = new ArrayList<>();
|
||||||
@Getter @Setter private List<String> extensionDomains = new ArrayList<>();
|
@Getter @Setter private List<String> extensionDomains = new ArrayList<>();
|
||||||
|
@ -264,6 +265,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
|
||||||
doImplicitFHIRPathStringConversion = other.doImplicitFHIRPathStringConversion;
|
doImplicitFHIRPathStringConversion = other.doImplicitFHIRPathStringConversion;
|
||||||
htmlInMarkdownCheck = other.htmlInMarkdownCheck;
|
htmlInMarkdownCheck = other.htmlInMarkdownCheck;
|
||||||
allowDoubleQuotesInFHIRPath = other.allowDoubleQuotesInFHIRPath;
|
allowDoubleQuotesInFHIRPath = other.allowDoubleQuotesInFHIRPath;
|
||||||
|
checkIPSCodes = other.checkIPSCodes;
|
||||||
locale = other.locale;
|
locale = other.locale;
|
||||||
igs.addAll(other.igs);
|
igs.addAll(other.igs);
|
||||||
extensionDomains.addAll(other.extensionDomains);
|
extensionDomains.addAll(other.extensionDomains);
|
||||||
|
@ -853,6 +855,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
|
||||||
validator.setAllowDoubleQuotesInFHIRPath(allowDoubleQuotesInFHIRPath);
|
validator.setAllowDoubleQuotesInFHIRPath(allowDoubleQuotesInFHIRPath);
|
||||||
validator.setNoUnicodeBiDiControlChars(noUnicodeBiDiControlChars);
|
validator.setNoUnicodeBiDiControlChars(noUnicodeBiDiControlChars);
|
||||||
validator.setDoImplicitFHIRPathStringConversion(doImplicitFHIRPathStringConversion);
|
validator.setDoImplicitFHIRPathStringConversion(doImplicitFHIRPathStringConversion);
|
||||||
|
validator.setCheckIPSCodes(checkIPSCodes);
|
||||||
if (format == FhirFormat.SHC) {
|
if (format == FhirFormat.SHC) {
|
||||||
igLoader.loadIg(getIgs(), getBinaries(), SHCParser.CURRENT_PACKAGE, true);
|
igLoader.loadIg(getIgs(), getBinaries(), SHCParser.CURRENT_PACKAGE, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,6 @@ public class ValidatorCli {
|
||||||
TimeTracker tt = new TimeTracker();
|
TimeTracker tt = new TimeTracker();
|
||||||
TimeTracker.Session tts = tt.start("Loading");
|
TimeTracker.Session tts = tt.start("Loading");
|
||||||
|
|
||||||
args = addAdditionalParamsForIpsParam(args);
|
|
||||||
setJavaSystemProxyParamsFromParams(args);
|
setJavaSystemProxyParamsFromParams(args);
|
||||||
|
|
||||||
Display.displayVersion(System.out);
|
Display.displayVersion(System.out);
|
||||||
|
@ -200,6 +199,8 @@ public class ValidatorCli {
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
final ValidatorCli validatorCli = new ValidatorCli(validationService);
|
final ValidatorCli validatorCli = new ValidatorCli(validationService);
|
||||||
|
|
||||||
|
args = addAdditionalParamsForIpsParam(args);
|
||||||
final CliContext cliContext = Params.loadCliContext(args);
|
final CliContext cliContext = Params.loadCliContext(args);
|
||||||
validatorCli.readParamsAndExecuteTask(cliContext, args);
|
validatorCli.readParamsAndExecuteTask(cliContext, args);
|
||||||
}
|
}
|
||||||
|
@ -259,6 +260,7 @@ public class ValidatorCli {
|
||||||
if (a.equals("-ips")) {
|
if (a.equals("-ips")) {
|
||||||
res.add("-version");
|
res.add("-version");
|
||||||
res.add("4.0");
|
res.add("4.0");
|
||||||
|
res.add("-check-ips-codes");
|
||||||
res.add("-ig");
|
res.add("-ig");
|
||||||
res.add("hl7.fhir.uv.ips#1.1.0");
|
res.add("hl7.fhir.uv.ips#1.1.0");
|
||||||
res.add("-profile");
|
res.add("-profile");
|
||||||
|
@ -266,6 +268,7 @@ public class ValidatorCli {
|
||||||
} else if (a.equals("-ips#")) {
|
} else if (a.equals("-ips#")) {
|
||||||
res.add("-version");
|
res.add("-version");
|
||||||
res.add("4.0");
|
res.add("4.0");
|
||||||
|
res.add("-check-ips-codes");
|
||||||
res.add("-ig");
|
res.add("-ig");
|
||||||
res.add("hl7.fhir.uv.ips#"+a.substring(5));
|
res.add("hl7.fhir.uv.ips#"+a.substring(5));
|
||||||
res.add("-profile");
|
res.add("-profile");
|
||||||
|
@ -273,6 +276,7 @@ public class ValidatorCli {
|
||||||
} else if (a.startsWith("-ips$")) {
|
} else if (a.startsWith("-ips$")) {
|
||||||
res.add("-version");
|
res.add("-version");
|
||||||
res.add("4.0");
|
res.add("4.0");
|
||||||
|
res.add("-check-ips-codes");
|
||||||
res.add("-ig");
|
res.add("-ig");
|
||||||
res.add("hl7.fhir.uv.ips#current$"+a.substring(5));
|
res.add("hl7.fhir.uv.ips#current$"+a.substring(5));
|
||||||
res.add("-profile");
|
res.add("-profile");
|
||||||
|
|
|
@ -56,6 +56,8 @@ public class CliContext {
|
||||||
private HtmlInMarkdownCheck htmlInMarkdownCheck = HtmlInMarkdownCheck.WARNING;
|
private HtmlInMarkdownCheck htmlInMarkdownCheck = HtmlInMarkdownCheck.WARNING;
|
||||||
@JsonProperty("allowDoubleQuotesInFHIRPath")
|
@JsonProperty("allowDoubleQuotesInFHIRPath")
|
||||||
private boolean allowDoubleQuotesInFHIRPath = false;
|
private boolean allowDoubleQuotesInFHIRPath = false;
|
||||||
|
@JsonProperty("checkIPSCodes")
|
||||||
|
private boolean checkIPSCodes;
|
||||||
@JsonProperty("langTransform")
|
@JsonProperty("langTransform")
|
||||||
private String langTransform = null;
|
private String langTransform = null;
|
||||||
@JsonProperty("map")
|
@JsonProperty("map")
|
||||||
|
@ -315,6 +317,14 @@ public class CliContext {
|
||||||
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
|
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCheckIPSCodes() {
|
||||||
|
return checkIPSCodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckIPSCodes(boolean checkIPSCodes) {
|
||||||
|
this.checkIPSCodes = checkIPSCodes;
|
||||||
|
}
|
||||||
|
|
||||||
@JsonProperty("locale")
|
@JsonProperty("locale")
|
||||||
public String getLanguageCode() {
|
public String getLanguageCode() {
|
||||||
return locale;
|
return locale;
|
||||||
|
@ -727,6 +737,7 @@ public class CliContext {
|
||||||
displayWarnings == that.displayWarnings &&
|
displayWarnings == that.displayWarnings &&
|
||||||
wantInvariantsInMessages == that.wantInvariantsInMessages &&
|
wantInvariantsInMessages == that.wantInvariantsInMessages &&
|
||||||
allowDoubleQuotesInFHIRPath == that.allowDoubleQuotesInFHIRPath &&
|
allowDoubleQuotesInFHIRPath == that.allowDoubleQuotesInFHIRPath &&
|
||||||
|
checkIPSCodes == that.checkIPSCodes &&
|
||||||
Objects.equals(extensions, that.extensions) &&
|
Objects.equals(extensions, that.extensions) &&
|
||||||
Objects.equals(map, that.map) &&
|
Objects.equals(map, that.map) &&
|
||||||
Objects.equals(htmlInMarkdownCheck, that.htmlInMarkdownCheck) &&
|
Objects.equals(htmlInMarkdownCheck, that.htmlInMarkdownCheck) &&
|
||||||
|
@ -768,7 +779,7 @@ public class CliContext {
|
||||||
return Objects.hash(doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching,
|
return Objects.hash(doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching,
|
||||||
noExtensibleBindingMessages, noInvariants, displayWarnings, wantInvariantsInMessages, map, output, outputSuffix, htmlOutput, txServer, sv, txLog, txCache, mapLog, lang, srcLang, tgtLang, fhirpath, snomedCT,
|
noExtensibleBindingMessages, noInvariants, displayWarnings, wantInvariantsInMessages, map, output, outputSuffix, htmlOutput, txServer, sv, txLog, txCache, mapLog, lang, srcLang, tgtLang, fhirpath, snomedCT,
|
||||||
targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars, watchMode, watchScanDelay, watchSettleTime,
|
targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars, watchMode, watchScanDelay, watchSettleTime,
|
||||||
htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath);
|
htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -821,6 +832,7 @@ public class CliContext {
|
||||||
", bundleValidationRules=" + bundleValidationRules +
|
", bundleValidationRules=" + bundleValidationRules +
|
||||||
", htmlInMarkdownCheck=" + htmlInMarkdownCheck +
|
", htmlInMarkdownCheck=" + htmlInMarkdownCheck +
|
||||||
", allowDoubleQuotesInFHIRPath=" + allowDoubleQuotesInFHIRPath +
|
", allowDoubleQuotesInFHIRPath=" + allowDoubleQuotesInFHIRPath +
|
||||||
|
", checkIPSCodes=" + checkIPSCodes +
|
||||||
", watchMode=" + watchMode +
|
", watchMode=" + watchMode +
|
||||||
", watchSettleTime=" + watchSettleTime +
|
", watchSettleTime=" + watchSettleTime +
|
||||||
", watchScanDelay=" + watchScanDelay +
|
", watchScanDelay=" + watchScanDelay +
|
||||||
|
|
|
@ -476,6 +476,7 @@ public class ValidationService {
|
||||||
validationEngine.setNoUnicodeBiDiControlChars(cliContext.isNoUnicodeBiDiControlChars());
|
validationEngine.setNoUnicodeBiDiControlChars(cliContext.isNoUnicodeBiDiControlChars());
|
||||||
validationEngine.setNoInvariantChecks(cliContext.isNoInvariants());
|
validationEngine.setNoInvariantChecks(cliContext.isNoInvariants());
|
||||||
validationEngine.setDisplayWarnings(cliContext.isDisplayWarnings());
|
validationEngine.setDisplayWarnings(cliContext.isDisplayWarnings());
|
||||||
|
validationEngine.setCheckIPSCodes(cliContext.isCheckIPSCodes());
|
||||||
validationEngine.setWantInvariantInMessage(cliContext.isWantInvariantsInMessages());
|
validationEngine.setWantInvariantInMessage(cliContext.isWantInvariantsInMessages());
|
||||||
validationEngine.setSecurityChecks(cliContext.isSecurityChecks());
|
validationEngine.setSecurityChecks(cliContext.isSecurityChecks());
|
||||||
validationEngine.setCrumbTrails(cliContext.isCrumbTrails());
|
validationEngine.setCrumbTrails(cliContext.isCrumbTrails());
|
||||||
|
|
|
@ -86,6 +86,7 @@ public class Params {
|
||||||
public static final String SRC_LANG = "-src-lang";
|
public static final String SRC_LANG = "-src-lang";
|
||||||
public static final String TGT_LANG = "-tgt-lang";
|
public static final String TGT_LANG = "-tgt-lang";
|
||||||
public static final String ALLOW_DOUBLE_QUOTES = "-allow-double-quotes-in-fhirpath";
|
public static final String ALLOW_DOUBLE_QUOTES = "-allow-double-quotes-in-fhirpath";
|
||||||
|
public static final String CHECK_IPS_CODES = "-check-ips-codes";
|
||||||
|
|
||||||
|
|
||||||
public static final String RUN_TESTS = "-run-tests";
|
public static final String RUN_TESTS = "-run-tests";
|
||||||
|
@ -247,7 +248,9 @@ public class Params {
|
||||||
} else if (args[i].equals(NO_EXTENSIBLE_BINDING_WARNINGS)) {
|
} else if (args[i].equals(NO_EXTENSIBLE_BINDING_WARNINGS)) {
|
||||||
cliContext.setNoExtensibleBindingMessages(true);
|
cliContext.setNoExtensibleBindingMessages(true);
|
||||||
} else if (args[i].equals(ALLOW_DOUBLE_QUOTES)) {
|
} else if (args[i].equals(ALLOW_DOUBLE_QUOTES)) {
|
||||||
cliContext.setAllowDoubleQuotesInFHIRPath(true);
|
cliContext.setAllowDoubleQuotesInFHIRPath(true);
|
||||||
|
} else if (args[i].equals(CHECK_IPS_CODES)) {
|
||||||
|
cliContext.setCheckIPSCodes(true);
|
||||||
} else if (args[i].equals(NO_UNICODE_BIDI_CONTROL_CHARS)) {
|
} else if (args[i].equals(NO_UNICODE_BIDI_CONTROL_CHARS)) {
|
||||||
cliContext.setNoUnicodeBiDiControlChars(true);
|
cliContext.setNoUnicodeBiDiControlChars(true);
|
||||||
} else if (args[i].equals(NO_INVARIANTS)) {
|
} else if (args[i].equals(NO_INVARIANTS)) {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.hl7.fhir.validation.codesystem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
|
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||||
|
import org.hl7.fhir.validation.BaseValidator;
|
||||||
|
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||||
|
|
||||||
|
public class CodeSystemChecker extends BaseValidator {
|
||||||
|
|
||||||
|
private boolean noDisplay = false;
|
||||||
|
private boolean hasDisplay = false;
|
||||||
|
protected List<ValidationMessage> errors;
|
||||||
|
|
||||||
|
protected CodeSystemChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List<ValidationMessage> errors) {
|
||||||
|
super(context, xverManager, debug);
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkConcept(String code, String display) {
|
||||||
|
if (Utilities.noString(display)) {
|
||||||
|
noDisplay = true;
|
||||||
|
} else {
|
||||||
|
hasDisplay = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finish(Element inc, NodeStack stack) {
|
||||||
|
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noDisplay && hasDisplay), I18nConstants.VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,76 +0,0 @@
|
||||||
package org.hl7.fhir.validation.codesystem;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2011+, HL7, Inc.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
* Neither the name of HL7 nor the names of its contributors may be used to
|
|
||||||
endorse or promote products derived from this software without specific
|
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
||||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
||||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
|
||||||
import org.hl7.fhir.r5.model.CodeSystem;
|
|
||||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
|
||||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
|
||||||
import org.hl7.fhir.validation.BaseValidator;
|
|
||||||
|
|
||||||
public class CodeSystemValidator extends BaseValidator {
|
|
||||||
|
|
||||||
public CodeSystemValidator(IWorkerContext context, XVerExtensionManager xverManager) {
|
|
||||||
super(context, xverManager, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ValidationMessage> validate(CodeSystem cs, boolean forBuild) {
|
|
||||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
|
||||||
|
|
||||||
// this is an invariant on CodeSystem, but the invariant is wrong in R3, and doesn't work
|
|
||||||
checkCodesUnique(cs, errors);
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkCodesUnique(CodeSystem cs, List<ValidationMessage> errors) {
|
|
||||||
Set<String> codes = new HashSet<String>();
|
|
||||||
checkCodes(codes, cs.getConcept(), "CodeSystem.where(id = '"+cs.getId()+"')", errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkCodes(Set<String> codes, List<ConceptDefinitionComponent> list, String path, List<ValidationMessage> errors) {
|
|
||||||
for (ConceptDefinitionComponent cc : list) {
|
|
||||||
String npath = path+".concept.where(code = '"+cc.getCode()+"')";
|
|
||||||
if (codes.contains(cc.getCode())) {
|
|
||||||
rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, npath, false, "Duplicate Code "+cc.getCode());
|
|
||||||
}
|
|
||||||
codes.add(cc.getCode());
|
|
||||||
checkCodes(codes, cc.getConcept(), npath, errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
package org.hl7.fhir.validation.codesystem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest;
|
||||||
|
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r5.model.Coding;
|
||||||
|
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||||
|
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||||
|
import org.hl7.fhir.validation.BaseValidator;
|
||||||
|
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||||
|
|
||||||
|
public class CodingsObserver extends BaseValidator {
|
||||||
|
|
||||||
|
private class CodingUsage {
|
||||||
|
|
||||||
|
private NodeStack stack;
|
||||||
|
private Coding c;
|
||||||
|
|
||||||
|
public CodingUsage(NodeStack stack, Coding c) {
|
||||||
|
this.stack = stack;
|
||||||
|
this.c = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodingsObserver(IWorkerContext context, XVerExtensionManager xverManager, boolean debug) {
|
||||||
|
super(context, xverManager, debug);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IWorkerContext context;
|
||||||
|
private List<CodingUsage> list = new ArrayList<>();
|
||||||
|
private boolean checkIPSCodes;
|
||||||
|
|
||||||
|
public void seeCode(NodeStack stack, String system, String version, String code, String display) {
|
||||||
|
seeCode(stack, new Coding().setSystem(system).setCode(code).setVersion(version).setDisplay(display));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCheckIPSCodes() {
|
||||||
|
return checkIPSCodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckIPSCodes(boolean checkIPSCodes) {
|
||||||
|
this.checkIPSCodes = checkIPSCodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void seeCode(NodeStack stack, CodeableConcept cc) {
|
||||||
|
for (Coding c : cc.getCoding()) {
|
||||||
|
seeCode(stack, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void seeCode(NodeStack stack, Coding c) {
|
||||||
|
list.add(new CodingUsage(stack, c));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finish(List<ValidationMessage> errors, NodeStack rootStack) {
|
||||||
|
if (checkIPSCodes) {
|
||||||
|
System.out.println("");
|
||||||
|
System.out.println("Checking SCT codes for IPS");
|
||||||
|
|
||||||
|
Set<String> snomedCTCodes = new HashSet<>();
|
||||||
|
for (CodingUsage c : list) {
|
||||||
|
if ("http://snomed.info/sct".equals(c.c.getSystem()) && c.c.getCode() != null) {
|
||||||
|
snomedCTCodes.add(c.c.getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!snomedCTCodes.isEmpty()) {
|
||||||
|
Map<String, String> nonIPSCodes = checkSCTCodes(snomedCTCodes);
|
||||||
|
if (!nonIPSCodes.isEmpty()) {
|
||||||
|
for (String s : nonIPSCodes.keySet()) {
|
||||||
|
hint(errors, "2023-07-25", IssueType.BUSINESSRULE, rootStack, false, I18nConstants.CS_SCT_IPS_NOT_IPS, s, nonIPSCodes.get(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("Done Checking SCT codes for IPS");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> checkSCTCodes(Set<String> codes) {
|
||||||
|
List<CodingValidationRequest> serverList = new ArrayList<>();
|
||||||
|
for (String s : codes) {
|
||||||
|
serverList.add(new CodingValidationRequest(new Coding("http://snomed.info/sct", s, null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
context.validateCodeBatchByRef(null, serverList, "http://terminology.hl7.org/ValueSet/snomed-intl-ips");
|
||||||
|
|
||||||
|
Map<String, String> results = new HashMap<>();
|
||||||
|
|
||||||
|
for (CodingValidationRequest vr : serverList) {
|
||||||
|
if (!vr.getResult().isOk()) {
|
||||||
|
results.put(vr.getCoding().getCode(), vr.getResult().getDisplay() != null ? vr.getResult().getDisplay() : vr.getCoding().getDisplay());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.hl7.fhir.validation.codesystem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
|
|
||||||
|
public class GeneralCodeSystemChecker extends CodeSystemChecker {
|
||||||
|
|
||||||
|
public GeneralCodeSystemChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug,
|
||||||
|
List<ValidationMessage> errors) {
|
||||||
|
super(context, xverManager, debug, errors);
|
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.hl7.fhir.validation.codesystem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
|
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||||
|
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||||
|
|
||||||
|
public class SnomedCTChecker extends CodeSystemChecker {
|
||||||
|
private boolean noTag = false;
|
||||||
|
private boolean hasTag = false;
|
||||||
|
|
||||||
|
public SnomedCTChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List<ValidationMessage> errors) {
|
||||||
|
super(context, xverManager, debug, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkConcept(String code, String display) {
|
||||||
|
super.checkConcept(code, display);
|
||||||
|
if (!Utilities.noString(display)) {
|
||||||
|
boolean tagged = display.endsWith(")") && display.indexOf("(") > display.length() - 20;
|
||||||
|
if (tagged) {
|
||||||
|
hasTag = true;
|
||||||
|
} else {
|
||||||
|
noTag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void finish(Element inc, NodeStack stack) {
|
||||||
|
super.finish(inc, stack);
|
||||||
|
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noTag && hasTag), I18nConstants.VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED);
|
||||||
|
}
|
||||||
|
}
|
|
@ -185,6 +185,7 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
import org.hl7.fhir.validation.BaseValidator;
|
import org.hl7.fhir.validation.BaseValidator;
|
||||||
import org.hl7.fhir.validation.cli.model.HtmlInMarkdownCheck;
|
import org.hl7.fhir.validation.cli.model.HtmlInMarkdownCheck;
|
||||||
import org.hl7.fhir.validation.cli.utils.QuestionnaireMode;
|
import org.hl7.fhir.validation.cli.utils.QuestionnaireMode;
|
||||||
|
import org.hl7.fhir.validation.codesystem.CodingsObserver;
|
||||||
import org.hl7.fhir.validation.instance.type.BundleValidator;
|
import org.hl7.fhir.validation.instance.type.BundleValidator;
|
||||||
import org.hl7.fhir.validation.instance.type.CodeSystemValidator;
|
import org.hl7.fhir.validation.instance.type.CodeSystemValidator;
|
||||||
import org.hl7.fhir.validation.instance.type.ConceptMapValidator;
|
import org.hl7.fhir.validation.instance.type.ConceptMapValidator;
|
||||||
|
@ -504,6 +505,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
private ValidationOptions baseOptions = new ValidationOptions();
|
private ValidationOptions baseOptions = new ValidationOptions();
|
||||||
private Map<String, CanonicalResourceLookupResult> crLookups = new HashMap<>();
|
private Map<String, CanonicalResourceLookupResult> crLookups = new HashMap<>();
|
||||||
private boolean logProgress;
|
private boolean logProgress;
|
||||||
|
private CodingsObserver codingObserver;
|
||||||
|
|
||||||
public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices, XVerExtensionManager xverManager) {
|
public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices, XVerExtensionManager xverManager) {
|
||||||
super(theContext, xverManager, false);
|
super(theContext, xverManager, false);
|
||||||
|
@ -518,6 +520,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
source = Source.InstanceValidator;
|
source = Source.InstanceValidator;
|
||||||
fpe.setDoNotEnforceAsSingletonRule(!VersionUtilities.isR5VerOrLater(theContext.getVersion()));
|
fpe.setDoNotEnforceAsSingletonRule(!VersionUtilities.isR5VerOrLater(theContext.getVersion()));
|
||||||
fpe.setAllowDoubleQuotes(allowDoubleQuotesInFHIRPath);
|
fpe.setAllowDoubleQuotes(allowDoubleQuotesInFHIRPath);
|
||||||
|
codingObserver = new CodingsObserver(theContext, xverManager, debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -911,6 +914,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
if (hintAboutNonMustSupport) {
|
if (hintAboutNonMustSupport) {
|
||||||
checkElementUsage(errors, element, stack);
|
checkElementUsage(errors, element, stack);
|
||||||
}
|
}
|
||||||
|
codingObserver.finish(errors, stack);
|
||||||
errors.removeAll(messagesToRemove);
|
errors.removeAll(messagesToRemove);
|
||||||
timeTracker.overall(t);
|
timeTracker.overall(t);
|
||||||
if (DEBUG_ELEMENT) {
|
if (DEBUG_ELEMENT) {
|
||||||
|
@ -6509,10 +6513,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
if (lang == null) {
|
if (lang == null) {
|
||||||
lang = "en"; // ubiquitious default languauge
|
lang = "en"; // ubiquitious default languauge
|
||||||
}
|
}
|
||||||
|
codingObserver.seeCode(stack, system, version, code, display);
|
||||||
return context.validateCode(baseOptions.withLanguage(lang), system, version, code, checkDisplay ? display : null);
|
return context.validateCode(baseOptions.withLanguage(lang), system, version, code, checkDisplay ? display : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, Coding c, boolean checkMembership) {
|
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, Coding c, boolean checkMembership) {
|
||||||
|
codingObserver.seeCode(stack, c);
|
||||||
if (checkMembership) {
|
if (checkMembership) {
|
||||||
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), c, valueset);
|
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), c, valueset);
|
||||||
} else {
|
} else {
|
||||||
|
@ -6521,6 +6527,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, CodeableConcept cc, boolean vsOnly) {
|
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, CodeableConcept cc, boolean vsOnly) {
|
||||||
|
codingObserver.seeCode(stack, cc);
|
||||||
if (vsOnly) {
|
if (vsOnly) {
|
||||||
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), cc, valueset);
|
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), cc, valueset);
|
||||||
} else {
|
} else {
|
||||||
|
@ -6657,6 +6664,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
baseOptions.setDisplayWarningMode(displayWarnings);
|
baseOptions.setDisplayWarningMode(displayWarnings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCheckIPSCodes() {
|
||||||
|
return codingObserver.isCheckIPSCodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckIPSCodes(boolean checkIPSCodes) {
|
||||||
|
codingObserver.setCheckIPSCodes(checkIPSCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public InstanceValidator setForPublication(boolean forPublication) {
|
public InstanceValidator setForPublication(boolean forPublication) {
|
||||||
this.forPublication = forPublication;
|
this.forPublication = forPublication;
|
||||||
|
|
|
@ -531,7 +531,7 @@ public class MeasureValidator extends BaseValidator {
|
||||||
CodeableConcept cc = ObjectConverter.readAsCodeableConcept(mrgs.getNamedChild("code"));
|
CodeableConcept cc = ObjectConverter.readAsCodeableConcept(mrgs.getNamedChild("code"));
|
||||||
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrgs.line(), mrgs.col(), ns.getLiteralPath(), cc != null, I18nConstants.MEASURE_MR_GRP_POP_NO_CODE)) {
|
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrgs.line(), mrgs.col(), ns.getLiteralPath(), cc != null, I18nConstants.MEASURE_MR_GRP_POP_NO_CODE)) {
|
||||||
MeasureGroupStratifierComponent mgs = getGroupStratifierForCode(cc, mg);
|
MeasureGroupStratifierComponent mgs = getGroupStratifierForCode(cc, mg);
|
||||||
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrg.line(), mrg.col(), ns.getLiteralPath(), mgs != null, I18nConstants.MEASURE_MR_GRP_POP_UNK_CODE)) {
|
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrg.line(), mrg.col(), ns.getLiteralPath(), mgs != null, I18nConstants.MEASURE_MR_GRPST_POP_UNK_CODE)) {
|
||||||
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrg.line(), mrg.col(), ns.getLiteralPath(), !strats.contains(mgs), I18nConstants.MEASURE_MR_GRP_POP_DUPL_CODE)) {
|
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrg.line(), mrg.col(), ns.getLiteralPath(), !strats.contains(mgs), I18nConstants.MEASURE_MR_GRP_POP_DUPL_CODE)) {
|
||||||
strats.add(mgs);
|
strats.add(mgs);
|
||||||
ok = validateMeasureReportGroupStratifier(hostContext, m, mgs, errors, mrgs, ns, inProgress) && ok;
|
ok = validateMeasureReportGroupStratifier(hostContext, m, mgs, errors, mrgs, ns, inProgress) && ok;
|
||||||
|
|
|
@ -22,76 +22,20 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||||
import org.hl7.fhir.validation.BaseValidator;
|
import org.hl7.fhir.validation.BaseValidator;
|
||||||
import org.hl7.fhir.validation.TimeTracker;
|
import org.hl7.fhir.validation.TimeTracker;
|
||||||
import org.hl7.fhir.validation.instance.InstanceValidator;
|
import org.hl7.fhir.validation.instance.InstanceValidator;
|
||||||
import org.hl7.fhir.validation.instance.type.ValueSetValidator.SystemLevelValidator;
|
|
||||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||||
|
import org.hl7.fhir.validation.codesystem.CodeSystemChecker;
|
||||||
|
import org.hl7.fhir.validation.codesystem.GeneralCodeSystemChecker;
|
||||||
|
import org.hl7.fhir.validation.codesystem.SnomedCTChecker;
|
||||||
|
|
||||||
public class ValueSetValidator extends BaseValidator {
|
public class ValueSetValidator extends BaseValidator {
|
||||||
|
|
||||||
public class SystemLevelValidator {
|
private CodeSystemChecker getSystemValidator(String system, List<ValidationMessage> errors) {
|
||||||
protected List<ValidationMessage> errors;
|
|
||||||
protected Element inc;
|
|
||||||
protected NodeStack stack;
|
|
||||||
|
|
||||||
private boolean noDisplay = false;
|
|
||||||
private boolean hasDisplay = false;
|
|
||||||
protected SystemLevelValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
|
|
||||||
super();
|
|
||||||
this.errors = errors;
|
|
||||||
this.inc = inc;
|
|
||||||
this.stack = stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkConcept(String code, String display) {
|
|
||||||
if (Utilities.noString(display)) {
|
|
||||||
noDisplay = true;
|
|
||||||
} else {
|
|
||||||
hasDisplay = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish() {
|
|
||||||
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noDisplay && hasDisplay), I18nConstants.VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SnomedCTValidator extends SystemLevelValidator {
|
|
||||||
private boolean noTag = false;
|
|
||||||
private boolean hasTag = false;
|
|
||||||
|
|
||||||
protected SnomedCTValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
|
|
||||||
super(errors, inc, stack);
|
|
||||||
}
|
|
||||||
public void checkConcept(String code, String display) {
|
|
||||||
super.checkConcept(code, display);
|
|
||||||
if (!Utilities.noString(display)) {
|
|
||||||
boolean tagged = display.endsWith(")") && display.indexOf("(") > display.length() - 20;
|
|
||||||
if (tagged) {
|
|
||||||
hasTag = true;
|
|
||||||
} else {
|
|
||||||
noTag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void finish() {
|
|
||||||
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noTag && hasTag), I18nConstants.VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GeneralValidator extends SystemLevelValidator {
|
|
||||||
|
|
||||||
protected GeneralValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
|
|
||||||
super(errors, inc, stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private SystemLevelValidator getSystemValidator(String system, List<ValidationMessage> errors, Element inc, NodeStack stack) {
|
|
||||||
if (system == null) {
|
if (system == null) {
|
||||||
return new GeneralValidator(errors, inc, stack);
|
return new GeneralCodeSystemChecker(context, xverManager, debug, errors);
|
||||||
}
|
}
|
||||||
switch (system) {
|
switch (system) {
|
||||||
case "http://snomed.info/sct" :return new SnomedCTValidator(errors, inc, stack);
|
case "http://snomed.info/sct" :return new SnomedCTChecker(context, xverManager, debug, errors);
|
||||||
default: return new GeneralValidator(errors, inc, stack);
|
default: return new GeneralCodeSystemChecker(context, xverManager, debug, errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +153,7 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
List<Element> concepts = include.getChildrenByName("concept");
|
List<Element> concepts = include.getChildrenByName("concept");
|
||||||
List<Element> filters = include.getChildrenByName("filter");
|
List<Element> filters = include.getChildrenByName("filter");
|
||||||
|
|
||||||
SystemLevelValidator slv = getSystemValidator(system, errors, include, stack);
|
CodeSystemChecker slv = getSystemValidator(system, errors);
|
||||||
if (!Utilities.noString(system)) {
|
if (!Utilities.noString(system)) {
|
||||||
boolean systemOk = true;
|
boolean systemOk = true;
|
||||||
int cc = 0;
|
int cc = 0;
|
||||||
|
@ -256,7 +200,7 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
}
|
}
|
||||||
cf++;
|
cf++;
|
||||||
}
|
}
|
||||||
slv.finish();
|
slv.finish(include, stack);
|
||||||
} else {
|
} else {
|
||||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING);
|
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +208,7 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean validateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stackInc, NodeStack stack, String system, String version, SystemLevelValidator slv) {
|
private boolean validateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stackInc, NodeStack stack, String system, String version, CodeSystemChecker slv) {
|
||||||
String code = concept.getChildValue("code");
|
String code = concept.getChildValue("code");
|
||||||
String display = concept.getChildValue("display");
|
String display = concept.getChildValue("display");
|
||||||
slv.checkConcept(code, display);
|
slv.checkConcept(code, display);
|
||||||
|
@ -305,7 +249,7 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VSCodingValidationRequest prepareValidateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version, SystemLevelValidator slv) {
|
private VSCodingValidationRequest prepareValidateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version, CodeSystemChecker slv) {
|
||||||
String code = concept.getChildValue("code");
|
String code = concept.getChildValue("code");
|
||||||
String display = concept.getChildValue("display");
|
String display = concept.getChildValue("display");
|
||||||
slv.checkConcept(code, display);
|
slv.checkConcept(code, display);
|
||||||
|
@ -317,7 +261,7 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
return new VSCodingValidationRequest(stack, c);
|
return new VSCodingValidationRequest(stack, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element filter, NodeStack push, String system, String version, SystemLevelValidator slv) {
|
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element filter, NodeStack push, String system, String version, CodeSystemChecker slv) {
|
||||||
//
|
//
|
||||||
// String display = concept.getChildValue("display");
|
// String display = concept.getChildValue("display");
|
||||||
// slv.checkConcept(code, display);
|
// slv.checkConcept(code, display);
|
||||||
|
|
Loading…
Reference in New Issue