Improve MeasureReport validation for checking subject count
This commit is contained in:
parent
37006946cd
commit
512f70aa18
|
@ -1093,4 +1093,9 @@ public class I18nConstants {
|
|||
public static final String IG_DEPENDENCY_EXCEPTION = "IG_DEPENDENCY_EXCEPTION";
|
||||
public static final String IG_DEPENDENCY_PACKAGE_UNKNOWN = "IG_DEPENDENCY_PACKAGE_UNKNOWN";
|
||||
public static final String NDJSON_EMPTY_LINE_WARNING = "NDJSON_EMPTY_LINE_WARNING";
|
||||
public static final String MEASURE_MR_GRP_POP_COUNT_CANT_CHECK = "MEASURE_MR_GRP_POP_COUNT_CANT_CHECK";
|
||||
public static final String MEASURE_MR_GRP_POP_COUNT_NO_REF = "MEASURE_MR_GRP_POP_COUNT_NO_REF";
|
||||
public static final String MEASURE_MR_GRP_POP_COUNT_UNRESOLVED = "MEASURE_MR_GRP_POP_COUNT_UNRESOLVED";
|
||||
public static final String MEASURE_MR_GRP_POP_COUNT_NO_REF_RES = "MEASURE_MR_GRP_POP_COUNT_NO_REF_RES";
|
||||
public static final String MEASURE_MR_GRP_POP_COUNT_REF_UNPROCESSIBLE = "MEASURE_MR_GRP_POP_COUNT_REF_UNPROCESSIBLE";
|
||||
}
|
||||
|
|
|
@ -371,6 +371,11 @@ MEASURE_MR_GRP_NO_CODE = Group should have a code that matches the group definit
|
|||
MEASURE_MR_GRP_NO_USABLE_CODE = None of the codes provided are usable for comparison - need both system and code on at least one code
|
||||
MEASURE_MR_GRP_NO_WRONG_CODE = The code provided ({0}) does not match the code specified in the measure report ({1})
|
||||
MEASURE_MR_GRP_POP_COUNT_MISMATCH = Mismatch between count {0} and number of subjects {1}
|
||||
MEASURE_MR_GRP_POP_COUNT_CANT_CHECK = Unable to check the stated count {0} because the subject list cannot be fully processed ({1})
|
||||
MEASURE_MR_GRP_POP_COUNT_NO_REF = Subject reference has no actual reference
|
||||
MEASURE_MR_GRP_POP_COUNT_UNRESOLVED = Subject reference {0} could not be resolved, and the apparent type is {1} which could not be processed
|
||||
MEASURE_MR_GRP_POP_COUNT_NO_REF_RES = Subject reference {0} could not be resolved, and so could not be processed
|
||||
MEASURE_MR_GRP_POP_COUNT_REF_UNPROCESSIBLE = Subject reference {0} resolved to a {1}, which could not be processed
|
||||
MEASURE_MR_GRP_POP_DUPL_CODE = The code for this group population is duplicated with another group
|
||||
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_COUNT = Count should be present for reports where type is not ''subject-list''
|
||||
|
|
|
@ -4564,7 +4564,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return true;
|
||||
}
|
||||
|
||||
private ResolvedReference localResolve(String ref, NodeStack stack, List<ValidationMessage> errors, String path, Element rootResource, Element groupingResource, Element source, BooleanHolder bh) {
|
||||
public ResolvedReference localResolve(String ref, NodeStack stack, List<ValidationMessage> errors, String path, Element rootResource, Element groupingResource, Element source, BooleanHolder bh) {
|
||||
if (ref.startsWith("#")) {
|
||||
// work back through the parent list, tracking the stack as we go
|
||||
// really, there should only be one level for this (contained resources cannot contain
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.hl7.fhir.r5.model.Measure.MeasureGroupStratifierComponent;
|
|||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.renderers.DataRenderer;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.FhirPublication;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
|
@ -32,7 +33,10 @@ import org.hl7.fhir.utilities.validation.ValidationMessage;
|
|||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.xml.XMLUtil;
|
||||
import org.hl7.fhir.validation.BaseValidator;
|
||||
import org.hl7.fhir.validation.BaseValidator.BooleanHolder;
|
||||
import org.hl7.fhir.validation.instance.InstanceValidator;
|
||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||
import org.hl7.fhir.validation.instance.utils.ResolvedReference;
|
||||
import org.hl7.fhir.validation.instance.utils.ValidationContext;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
|
@ -493,21 +497,95 @@ public class MeasureValidator extends BaseValidator {
|
|||
|
||||
private boolean validateMeasureReportGroupPopulation(ValidationContext hostContext, MeasureContext m, MeasureGroupPopulationComponent mgp, List<ValidationMessage> errors, Element mrgp, NodeStack ns, boolean inProgress) {
|
||||
boolean ok = true;
|
||||
List<Element> sr = mrgp.getChildrenByName("subjectResults");
|
||||
List<Element> srl = mrgp.getChildrenByName("subjectResults");
|
||||
if ("subject-list".equals(m.reportType())) {
|
||||
try {
|
||||
int subCount = 0;
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("; ");
|
||||
for (Element sr : srl) {
|
||||
subCount = addToSubjectCount(subCount, hostContext, sr, m, ns, b);
|
||||
}
|
||||
if (mrgp.hasChild("count")) {
|
||||
int c = Integer.parseInt(mrgp.getChildValue("count"));
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrgp.line(), mrgp.col(), ns.getLiteralPath(), c == sr.size(), I18nConstants.MEASURE_MR_GRP_POP_COUNT_MISMATCH, c, sr.size()) && ok;
|
||||
if (subCount > -1) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrgp.line(), mrgp.col(), ns.getLiteralPath(), c == subCount, I18nConstants.MEASURE_MR_GRP_POP_COUNT_MISMATCH, c, subCount) && ok;
|
||||
} else {
|
||||
hint(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrgp.line(), mrgp.col(), ns.getLiteralPath(), false, I18nConstants.MEASURE_MR_GRP_POP_COUNT_CANT_CHECK, c, b.toString());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// nothing; that'll be because count is not valid, and that's a different error or its missing and we don't care
|
||||
}
|
||||
} else {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrgp.line(), mrgp.col(), ns.getLiteralPath(), sr.size() == 0, I18nConstants.MEASURE_MR_GRP_POP_NO_SUBJECTS) && ok;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrgp.line(), mrgp.col(), ns.getLiteralPath(), srl.size() == 0, I18nConstants.MEASURE_MR_GRP_POP_NO_SUBJECTS) && ok;
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, mrgp.line(), mrgp.col(), ns.getLiteralPath(), mrgp.hasChild("count", false), I18nConstants.MEASURE_MR_GRP_POP_NO_COUNT);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private int addToSubjectCount(int subCount, ValidationContext valContext, Element sr, MeasureContext m, NodeStack ns, CommaSeparatedStringBuilder b) throws FHIRException, IOException {
|
||||
if (subCount < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String ref = sr.getNamedChildValue("reference");
|
||||
if (ref == null) {
|
||||
b.append(context.formatMessage(I18nConstants.MEASURE_MR_GRP_POP_COUNT_NO_REF));
|
||||
return -1;
|
||||
}
|
||||
BooleanHolder bh = new BooleanHolder();
|
||||
ResolvedReference rr = ((InstanceValidator) parent).localResolve(ref, ns, new ArrayList<>(), ns.getLiteralPath(), valContext.getRootResource(), valContext.getGroupingResource(), sr, bh);
|
||||
Element tgt;
|
||||
if (rr != null) {
|
||||
tgt = rr.getResource();
|
||||
} else {
|
||||
tgt = fetcher.fetch(((InstanceValidator) parent), valContext.getAppContext(), ref);
|
||||
}
|
||||
if (tgt == null) {
|
||||
// we couldn't resolve it, but we'll draw our own conclusion from the literal URL if we can.
|
||||
String[] parts = ref.split("\\/");
|
||||
if (parts.length == 2 && context.getResourceNamesAsSet().contains(parts[0])) {
|
||||
switch (parts[0]) {
|
||||
case "Patient":
|
||||
case "Practitioner":
|
||||
case "Person":
|
||||
case "PractitionerRole":
|
||||
case "RelatedPerson":
|
||||
return subCount + 1;
|
||||
case "List":
|
||||
b.append(context.formatMessage(I18nConstants.MEASURE_MR_GRP_POP_COUNT_UNRESOLVED, "List", ref));
|
||||
return -1; // for now
|
||||
case "Group":
|
||||
b.append(context.formatMessage(I18nConstants.MEASURE_MR_GRP_POP_COUNT_UNRESOLVED, "Group", ref));
|
||||
return -1; // for now
|
||||
default:
|
||||
b.append(context.formatMessage(I18nConstants.MEASURE_MR_GRP_POP_COUNT_UNRESOLVED, parts[0], ref));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// add information / hint?
|
||||
b.append(context.formatMessage(I18nConstants.MEASURE_MR_GRP_POP_COUNT_NO_REF_RES, ref));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
switch (tgt.fhirType()) {
|
||||
case "Patient":
|
||||
case "Practitioner":
|
||||
case "Person":
|
||||
case "PractitionerRole":
|
||||
case "RelatedPerson":
|
||||
return subCount + 1;
|
||||
case "List":
|
||||
return subCount + tgt.getChildren("entry").size();
|
||||
case "Group":
|
||||
b.append(context.formatMessage(I18nConstants.MEASURE_MR_GRP_POP_COUNT_REF_UNPROCESSIBLE, "Group", ref));
|
||||
return -1; // for now
|
||||
default:
|
||||
b.append(context.formatMessage(I18nConstants.MEASURE_MR_GRP_POP_COUNT_REF_UNPROCESSIBLE, tgt.fhirType(), ref));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateMeasureReportGroupStratifiers(ValidationContext hostContext, MeasureContext m, MeasureGroupComponent mg, List<ValidationMessage> errors, Element mrg, NodeStack stack, boolean inProgress) {
|
||||
boolean ok = true;
|
||||
|
||||
|
|
|
@ -3587,10 +3587,67 @@ v: {
|
|||
"code" : "en-AU",
|
||||
"system" : "urn:ietf:bcp:47",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260",
|
||||
"code" : "1",
|
||||
"display" : "Surgery Case"
|
||||
}, "url": "http://terminology.hl7.org/ValueSet/v3-ActEncounterCode", "version": "3.0.0", "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"code" : "1",
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260' could not be found, so the code cannot be validated; The provided code 'urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260#1 ('Surgery Case')' was not found in the value set 'http://terminology.hl7.org/ValueSet/v3-ActEncounterCode|3.0.0'",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r4"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
"details" : {
|
||||
"coding" : [{
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "not-found"
|
||||
}],
|
||||
"text" : "A definition for CodeSystem 'urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260' could not be found, so the code cannot be validated"
|
||||
},
|
||||
"location" : ["Coding.system"],
|
||||
"expression" : ["Coding.system"]
|
||||
},
|
||||
{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r4"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "code-invalid",
|
||||
"details" : {
|
||||
"coding" : [{
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "not-in-vs"
|
||||
}],
|
||||
"text" : "The provided code 'urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260#1 ('Surgery Case')' was not found in the value set 'http://terminology.hl7.org/ValueSet/v3-ActEncounterCode|3.0.0'"
|
||||
},
|
||||
"location" : ["Coding.code"],
|
||||
"expression" : ["Coding.code"]
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue