CodeSystem property validation improvements

This commit is contained in:
Grahame Grieve 2024-03-18 18:09:56 +11:00
parent 413c047b56
commit 30545d31c1
6 changed files with 72 additions and 4 deletions

View File

@ -234,6 +234,15 @@ public class CodeSystemUtilities extends TerminologyUtilities {
concept.addProperty().setCode(code).setValue(value);
}
public static void setProperty(CodeSystem cs, ConceptDefinitionComponent concept, String url, String code, DataType value) throws FHIRFormatError {
defineProperty(cs, code, propertyTypeForValue(value), url);
ConceptPropertyComponent p = getProperty(concept, code);
if (p != null)
p.setValue(value);
else
concept.addProperty().setCode(code).setValue(value);
}
private static PropertyType propertyTypeForValue(DataType value) {
if (value instanceof BooleanType) {
@ -262,6 +271,9 @@ public class CodeSystemUtilities extends TerminologyUtilities {
private static String defineProperty(CodeSystem cs, String code, PropertyType pt) {
String url = "http://hl7.org/fhir/concept-properties#"+code;
return defineProperty(cs, code, pt, url);
}
private static String defineProperty(CodeSystem cs, String code, PropertyType pt, String url) {
for (PropertyComponent p : cs.getProperty()) {
if (p.hasCode() && p.getCode().equals(code)) {
if (!p.getUri().equals(url)) {

View File

@ -2235,4 +2235,11 @@ public class Utilities {
return true;
}
public static String stripEoln(String text) {
if (text == null) {
return "";
}
return text.replace("\r\n", " ").replace("\n", " ").replace("\r", " ");
}
}

View File

@ -1101,6 +1101,7 @@ public class I18nConstants {
public static final String VALUESET_BAD_FILTER_VALUE_HAS_COMMA = "VALUESET_BAD_FILTER_VALUE_HAS_COMMA";
public static final String VALUESET_BAD_FILTER_VALUE_VALID_REGEX = "VALUESET_BAD_FILTER_VALUE_VALID_REGEX";
public static final String VALUESET_BAD_PROPERTY_NO_REGEX = "VALUESET_BAD_PROPERTY_NO_REGEX";
public static final String CODESYSTEM_PROPERTY_CODE_WARNING = "CODESYSTEM_PROPERTY_CODE_WARNING";
}

View File

@ -1159,4 +1159,5 @@ VALUESET_BAD_FILTER_OP = The operation ''{0}'' is not allowed for property ''{1}
VALUESET_BAD_FILTER_VALUE_HAS_COMMA = The filter value has a comma, but the operation is different to 'in' and 'not-in', so the comma will be interpreted as part of the {0} value
VALUESET_BAD_FILTER_VALUE_VALID_REGEX = The value for a filter based on property ''{0}'' should be a valid regex, not ''{1}'' (err = ''{2}'')
VALUESET_BAD_PROPERTY_NO_REGEX = Cannot apply a regex filter to the property ''{0}'' (usually regex filters are applied to the codes, or a named property of the code system)
CODESYSTEM_PROPERTY_CODE_WARNING = If the type is ''code'', then the valueSet property should be provided to clarify what kind of code will be found in the element

View File

@ -3344,7 +3344,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
Set<String> refs = new HashSet<>();
int count = countTargetMatches(resource, ref, true, "$", refs);
if (count == 0) {
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE, href, xpath, node.allText());
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE, href, xpath, Utilities.stripEoln(node.allText()));
} else if (count > 1) {
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES, href, xpath, node.allText(), CommaSeparatedStringBuilder.join(", ", refs));
}
@ -5760,7 +5760,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wg != null && !url.contains("http://hl7.org/fhir/sid"), I18nConstants.VALIDATION_HL7_WG_NEEDED, ToolingExtensions.EXT_WORKGROUP)) {
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wg != null || url.contains("http://hl7.org/fhir/sid"), I18nConstants.VALIDATION_HL7_WG_NEEDED, ToolingExtensions.EXT_WORKGROUP)) {
HL7WorkGroup wgd = HL7WorkGroups.find(wg);
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wgd != null, I18nConstants.VALIDATION_HL7_WG_UNKNOWN, wg)) {
String rpub = "HL7 International / "+wgd.getName();

View File

@ -14,6 +14,7 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
@ -57,16 +58,30 @@ public class CodeSystemValidator extends BaseValidator {
}
public enum CodeValidationRule {
NO_VALIDATION, INTERNAL_CODE, VS_ERROR, VS_WARNING
}
public class PropertyDef {
private String uri;
private String code;
private String type;
private CodeValidationRule rule;
private String valueset;
protected PropertyDef(String uri, String code, String type) {
super();
this.uri = uri;
this.code = code;
this.type = type;
}
public void setCodeValidationRules(CodeValidationRule rule, String valueset) {
this.rule = rule;
this.valueset = valueset;
}
public String getUri() {
return uri;
}
@ -76,9 +91,14 @@ public class CodeSystemValidator extends BaseValidator {
public String getType() {
return type;
}
public String getValueset() {
return valueset;
}
}
private static final String VS_PROP_STATUS = null;
public CodeSystemValidator(BaseValidator parent) {
super(parent);
}
@ -252,8 +272,7 @@ public class CodeSystemValidator extends BaseValidator {
ukp = KnownProperty.ItemWeight;
break;
default:
ok = false;
rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_PROPERTY_BAD_HL7_URI, uri);
ok = rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), isBaseSpec(cs.getNamedChildValue("url")), I18nConstants.CODESYSTEM_PROPERTY_BAD_HL7_URI, uri);
}
}
}
@ -309,7 +328,31 @@ public class CodeSystemValidator extends BaseValidator {
if (type != null) {
ok = rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), type.equals(ukp.getType()), I18nConstants.CODESYSTEM_PROPERTY_URI_TYPE_MISMATCH, uri, ukp.getType(),type) && ok;
}
switch (ukp) {
case Child:
case Parent:
case PartOf:
case Synonym:
pd.setCodeValidationRules(CodeValidationRule.INTERNAL_CODE, null);
break;
case Status:
pd.setCodeValidationRules(CodeValidationRule.VS_WARNING, VS_PROP_STATUS);
break;
default:
break;
}
} else if ("code".equals(pd.getType())) {
if (property.hasExtension("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet")) {
pd.setCodeValidationRules(CodeValidationRule.VS_ERROR, property.getExtensionValue("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet").primitiveValue());
} else if (VersionUtilities.isR6Plus(context.getVersion())) {
hint(errors, "2024-03-18", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), ukp != null && type.equals(ukp.getType()), I18nConstants.CODESYSTEM_PROPERTY_CODE_WARNING);
} else {
}
} else if ("Coding".equals(pd.getType()) && property.hasExtension("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet")) {
pd.setCodeValidationRules(CodeValidationRule.VS_ERROR, property.getExtensionValue("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet").primitiveValue());
}
if (uri == null) {
if (ckp == null) {
hint(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_PROPERTY_UNKNOWN_CODE, code);
@ -323,6 +366,10 @@ public class CodeSystemValidator extends BaseValidator {
return ok;
}
private boolean isBaseSpec(String url) {
return url.startsWith("http://hl7.org/fhir/") && !url.substring(20).contains("/");
}
private boolean checkConcept(List<ValidationMessage> errors, Element cs, NodeStack stack, boolean caseSensitive, String hierarchyMeaning, CodeSystem csB, Element concept, Set<String> codes, Map<String, PropertyDef> properties) {
boolean ok = true;
String code = concept.getNamedChildValue("code");