CodeSystem validation around count and content

This commit is contained in:
Grahame Grieve 2023-08-15 19:34:24 +10:00
parent cc5069ccb3
commit ea4e592910
3 changed files with 56 additions and 6 deletions

View File

@ -966,6 +966,11 @@ public class I18nConstants {
public static final String MSG_DEPENDS_ON_EXTENSION = "MSG_DEPENDS_ON_EXTENSION"; public static final String MSG_DEPENDS_ON_EXTENSION = "MSG_DEPENDS_ON_EXTENSION";
public static final String MSG_DEPENDS_ON_PROFILE = "MSG_DEPENDS_ON_PROFILE"; public static final String MSG_DEPENDS_ON_PROFILE = "MSG_DEPENDS_ON_PROFILE";
public static final String VALIDATION_VAL_STATUS_INCONSISTENT = "VALIDATION_VAL_STATUS_INCONSISTENT"; public static final String VALIDATION_VAL_STATUS_INCONSISTENT = "VALIDATION_VAL_STATUS_INCONSISTENT";
public static final String CODESYSTEM_CS_COUNT_COMPLETE_WRONG = "CODESYSTEM_CS_COUNT_COMPLETE_WRONG";
public static final String CODESYSTEM_CS_COUNT_FRAGMENT_WRONG = "CODESYSTEM_CS_COUNT_FRAGMENT_WRONG";
public static final String CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO = "CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO";
public static final String CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG = "CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG";
public static final String CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED = "CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED";
} }

View File

@ -1024,5 +1024,11 @@ MSG_DEPENDS_ON_EXPERIMENTAL = The {0} {1} is an experimental resource
MSG_DEPENDS_ON_DRAFT = The {0} {1} is a draft resource MSG_DEPENDS_ON_DRAFT = The {0} {1} is a draft resource
MSG_DEPENDS_ON_EXTENSION = extension MSG_DEPENDS_ON_EXTENSION = extension
MSG_DEPENDS_ON_PROFILE = profile MSG_DEPENDS_ON_PROFILE = profile
VALIDATION_VAL_STATUS_INCONSISTENT = The resource status ''{0}'' amd the standards status ''{1}'' are not consistent VALIDATION_VAL_STATUS_INCONSISTENT = The resource status ''{0}'' and the standards status ''{1}'' are not consistent
CODESYSTEM_CS_COUNT_COMPLETE_WRONG = The code system is complete, but the number of concepts ({0}) does not match the stated total number ({1})
CODESYSTEM_CS_COUNT_FRAGMENT_WRONG = The code system is a fragment/example, but the number of concepts ({0}) exceeds or matches the stated total number ({1})
CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO = The code system has no content, but the exceeds the stated total number is 0 concepts - check that this isn't a complete code system that has no concepts, or update/remove the stated count
CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG = The code system supplement states the total number of concepts as {1}, but this is different to the underlying code system that states a value of {0}
CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED = The code system says it has no content present, but concepts are found

View File

@ -6,6 +6,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
@ -32,8 +33,9 @@ public class CodeSystemValidator extends BaseValidator {
String caseSensitive = cs.getNamedChildValue("caseSensitive"); String caseSensitive = cs.getNamedChildValue("caseSensitive");
String hierarchyMeaning = cs.getNamedChildValue("hierarchyMeaning"); String hierarchyMeaning = cs.getNamedChildValue("hierarchyMeaning");
String supp = cs.getNamedChildValue("supplements"); String supp = cs.getNamedChildValue("supplements");
int count = countConcepts(cs);
metaChecks(errors, cs, stack, url, content, caseSensitive, hierarchyMeaning, !Utilities.noString(supp));
metaChecks(errors, cs, stack, url, content, caseSensitive, hierarchyMeaning, !Utilities.noString(supp), count, supp);
String vsu = cs.getNamedChildValue("valueSet"); String vsu = cs.getNamedChildValue("valueSet");
if (!Utilities.noString(vsu)) { if (!Utilities.noString(vsu)) {
@ -51,7 +53,6 @@ public class CodeSystemValidator extends BaseValidator {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet()
&& !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), I18nConstants.CODESYSTEM_CS_VS_INCLUDEDETAILS, url, vsu) && ok; && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), I18nConstants.CODESYSTEM_CS_VS_INCLUDEDETAILS, url, vsu) && ok;
if (vs.hasExpansion()) { if (vs.hasExpansion()) {
int count = countConcepts(cs);
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getExpansion().getContains().size() == count, I18nConstants.CODESYSTEM_CS_VS_EXP_MISMATCH, url, vsu, count, vs.getExpansion().getContains().size()) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getExpansion().getContains().size() == count, I18nConstants.CODESYSTEM_CS_VS_EXP_MISMATCH, url, vsu, count, vs.getExpansion().getContains().size()) && ok;
} }
} else { } else {
@ -71,7 +72,11 @@ public class CodeSystemValidator extends BaseValidator {
List<Element> concepts = cs.getChildrenByName("concept"); List<Element> concepts = cs.getChildrenByName("concept");
int ce = 0; int ce = 0;
for (Element concept : concepts) { for (Element concept : concepts) {
ok = validateSupplementConcept(errors, concept, stack.push(concept, ce, null, null), supp, options) && ok; NodeStack nstack = stack.push(concept, ce, null, null);
if (ce == 0) {
rule(errors, "2023-08-15", IssueType.INVALID, nstack, !"not-present".equals(content), I18nConstants.CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED);
}
ok = validateSupplementConcept(errors, concept, nstack, supp, options) && ok;
ce++; ce++;
} }
} else { } else {
@ -121,7 +126,7 @@ public class CodeSystemValidator extends BaseValidator {
return true; return true;
} }
private void metaChecks(List<ValidationMessage> errors, Element cs, NodeStack stack, String url, String content, String caseSensitive, String hierarchyMeaning, boolean isSupplement) { private void metaChecks(List<ValidationMessage> errors, Element cs, NodeStack stack, String url, String content, String caseSensitive, String hierarchyMeaning, boolean isSupplement, int count, String supp) {
if (isSupplement) { if (isSupplement) {
if (!"supplement".equals(content)) { if (!"supplement".equals(content)) {
NodeStack s = stack.push(cs.getNamedChild("content"), -1, null, null); NodeStack s = stack.push(cs.getNamedChild("content"), -1, null, null);
@ -178,6 +183,40 @@ public class CodeSystemValidator extends BaseValidator {
} }
} }
} }
if (cs.hasChild("count")) {
int statedCount = Utilities.parseInt(cs.getNamedChildValue("count"), -1);
if (statedCount > -1 && content != null) { // error elsewhere
var nstack = stack.push(cs.getNamedChild("count"), -1, null, null);
switch (content) {
case "complete":
rule(errors, "2023-08-15", IssueType.INVALID, nstack, count == statedCount, I18nConstants.CODESYSTEM_CS_COUNT_COMPLETE_WRONG, count, statedCount);
break;
case "example":
case "fragment":
warning(errors, "2023-08-15", IssueType.INVALID, nstack, count < statedCount, I18nConstants.CODESYSTEM_CS_COUNT_FRAGMENT_WRONG, count, statedCount);
break;
case "not-present":
hint(errors, "2023-08-15", IssueType.INVALID, stack.push(cs.getNamedChild("concept"), -1, null, null), statedCount > 0, I18nConstants.CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO, statedCount);
break;
case "supplement":
CodeSystem css = context.fetchCodeSystem(supp);
if (css != null) {
rule(errors, "2023-08-15", IssueType.INVALID, nstack, count == css.getCount(), I18nConstants.CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG, css.getCount(), statedCount);
}
break;
default:
// do nothing
}
}
}
if ("not-present".equals(content)) {
List<Element> concepts = cs.getChildrenByName("concept");
if (concepts.size() > 0) {
rule(errors, "2023-08-15", IssueType.INVALID, stack.push(concepts.get(0), 0, null, null), false, I18nConstants.CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED);
}
}
} }