From 5cffd2e7f79d4de6321bc3c32a340b18c2c8d83e Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:56:33 -0700 Subject: [PATCH 1/9] measure def refactor --- .../cqf/fhir/cr/measure/common/GroupDef.java | 44 +- .../fhir/cr/measure/common/MeasureDef.java | 21 +- .../cr/measure/common/MeasureEvaluator.java | 6 +- .../cr/measure/constant/MeasureConstants.java | 2 + .../measure/dstu3/Dstu3MeasureBasisDef.java | 26 - .../measure/dstu3/Dstu3MeasureDefBuilder.java | 111 ++--- .../dstu3/Dstu3MeasureReportBuilder.java | 2 +- .../cr/measure/r4/R4CareGapsProcessor.java | 13 +- .../fhir/cr/measure/r4/R4MeasureBasisDef.java | 26 - .../cr/measure/r4/R4MeasureDefBuilder.java | 172 ++++--- .../cr/measure/r4/R4MeasureReportBuilder.java | 84 ++-- .../dstu3/Dstu3MeasureDefBuilderTest.java | 72 ++- .../dstu3/Dstu3MeasureReportBuilderTest.java | 20 +- .../cr/measure/r4/MeasureDefBuilderTest.java | 450 ++++++++++++++++++ .../measure/r4/R4MeasureDefBuilderTest.java | 86 ---- .../r4/R4MeasureReportBuilderTest.java | 56 --- .../cr/measure/dstu3/EXM105FHIR3Sample.json | 4 + ...05FHIR3SampleWithBoolenPopulationBias.json | 4 + .../EXM105FHIR3SampleWithoutExtension.json | 4 + ...easureBuilderSampleWithEmptyExtension.json | 56 --- ...uilderSampleWithPopulationBiasBoolean.json | 60 --- ...lderSampleWithPopulationBiasEncounter.json | 60 --- .../MeasureBuilderSampleWithoutExtension.json | 56 --- ...asure-RatioGroupBooleanAllPopulations.json | 108 +++++ .../Measure-RatioNoBasisAllPopulations.json | 102 ++++ .../fhir/cr/measure/r4/TemplateMeasure.json | 30 ++ 26 files changed, 1023 insertions(+), 652 deletions(-) delete mode 100644 cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureBasisDef.java delete mode 100644 cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureBasisDef.java create mode 100644 cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java delete mode 100644 cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilderTest.java delete mode 100644 cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilderTest.java delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithEmptyExtension.json delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithPopulationBiasBoolean.json delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithPopulationBiasEncounter.json delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithoutExtension.json create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioGroupBooleanAllPopulations.json create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioNoBasisAllPopulations.json create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/TemplateMeasure.json diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/GroupDef.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/GroupDef.java index 0c7bbe997..983601f9d 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/GroupDef.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/GroupDef.java @@ -12,9 +12,9 @@ public class GroupDef { private final List stratifiers; private final List populations; private final MeasureScoring measureScoring; - private final boolean useGroupDefImprovementNotation; - private final boolean isIncreaseImprovementNotation; - + private final boolean isGroupImpNotation; + private final CodeDef populationBasis; + private final CodeDef improvementNotation; private final Map> populationIndex; public GroupDef( @@ -23,16 +23,23 @@ public GroupDef( List stratifiers, List populations, MeasureScoring measureScoring, - boolean isIncreaseImprovementNotation, - boolean useGroupDefImprovementNotation) { + boolean isGroupImprovementNotation, + CodeDef improvementNotation, + CodeDef populationBasis) { + // this.id = id; this.code = code; this.stratifiers = stratifiers; this.populations = populations; this.populationIndex = index(populations); this.measureScoring = measureScoring; - this.isIncreaseImprovementNotation = isIncreaseImprovementNotation; - this.useGroupDefImprovementNotation = useGroupDefImprovementNotation; + this.isGroupImpNotation = isGroupImprovementNotation; + this.improvementNotation = improvementNotation; + this.populationBasis = populationBasis; + // this.isGroupPopulationBasis = isGroupPopulationBasis; + // this.isGroupScoring = isGroupScoring; + // this.isIncreaseImpNotation = isIncreaseImprovementNotation; + // this.isBooleanBasis = isBooleanBasis; } public String id() { @@ -77,10 +84,27 @@ public MeasureScoring measureScoring() { } public boolean isIncreaseImprovementNotation() { - return this.isIncreaseImprovementNotation; + if (getImprovementNotation() != null) { + return getImprovementNotation().code().equals("increase"); + } else { + // default response if null + return true; + } + } + + public boolean isGroupImprovementNotation() { + return this.isGroupImpNotation; + } + + public boolean isBooleanBasis() { + return getPopulationBasis().code().equals("boolean"); + } + + public CodeDef getPopulationBasis() { + return this.populationBasis; } - public boolean useGroupDefImprovementNotation() { - return this.useGroupDefImprovementNotation; + public CodeDef getImprovementNotation() { + return this.improvementNotation; } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureDef.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureDef.java index 5884a854d..683b16094 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureDef.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureDef.java @@ -11,24 +11,13 @@ public class MeasureDef { private Interval defaultMeasurementPeriod; private final List groups; private final List sdes; - private final boolean isBooleanBasis; - private final boolean useMeasureImpNotation; - public MeasureDef( - String id, - String url, - String version, - List groups, - List sdes, - boolean isBooleanBasis, - boolean useMeasureImprovementNotation) { + public MeasureDef(String id, String url, String version, List groups, List sdes) { this.id = id; this.url = url; this.version = version; this.groups = groups; this.sdes = sdes; - this.isBooleanBasis = isBooleanBasis; - this.useMeasureImpNotation = useMeasureImprovementNotation; } public String id() { @@ -54,12 +43,4 @@ public List sdes() { public List groups() { return this.groups; } - - public boolean isBooleanBasis() { - return this.isBooleanBasis; - } - - public boolean useMeasureImpNotation() { - return this.useMeasureImpNotation; - } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureEvaluator.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureEvaluator.java index c28e2aa52..5bed6ae11 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureEvaluator.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureEvaluator.java @@ -452,7 +452,7 @@ protected void evaluateProportion( evaluatePopulationMembership(subjectType, subjectId, numeratorExclusion, evaluationResult); } // Apply Exclusions and Exceptions - if (measureDef.isBooleanBasis()) { + if (groupDef.isBooleanBasis()) { // Remove Subject and Resource Exclusions if (denominatorExclusion != null) { denominator.getSubjects().removeAll(denominatorExclusion.getSubjects()); @@ -528,7 +528,7 @@ protected void evaluateContinuousVariable( subjectType, subjectId, groupDef.getSingle(MEASUREPOPULATIONEXCLUSION), evaluationResult); } // Apply Exclusions to Population - if (measureDef.isBooleanBasis()) { + if (groupDef.isBooleanBasis()) { if (measurePopulationExclusion != null) { measurePopulation.getSubjects().removeAll(measurePopulationExclusion.getSubjects()); measurePopulation.getResources().removeAll(measurePopulationExclusion.getResources()); @@ -545,7 +545,7 @@ protected void evaluateContinuousVariable( resource, measureObservation.expression(), measureObservation.getEvaluatedResources(), - measureDef.isBooleanBasis()); + groupDef.isBooleanBasis()); measureObservation.addResource(observationResult); } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/constant/MeasureConstants.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/constant/MeasureConstants.java index 953493e5a..e77860bd0 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/constant/MeasureConstants.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/constant/MeasureConstants.java @@ -6,6 +6,7 @@ public class MeasureConstants { private MeasureConstants() {} + public static final String CQFM_SCORING_SYSTEM_URL = "http://terminology.hl7.org/ValueSet/measure-scoring"; public static final String CQFM_SCORING_EXT_URL = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-scoring"; // this is used on Measure resources to indicate to $care-gaps and $care-list that the measure is compatible for the @@ -39,6 +40,7 @@ private MeasureConstants() {} "http://hl7.org/fhir/5.0/StructureDefinition/extension-MeasureReport.operationOutcome.reference"; public static final String MEASUREMENT_PERIOD_PARAMETER_NAME = "Measurement Period"; public static final String FHIR_MODEL_URI = "http://hl7.org/fhir"; + public static final String FHIR_ALL_TYPES_SYSTEM_URL = "http://hl7.org/fhir/fhir-types"; public static final String POPULATION_BASIS_URL = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis"; public static final String EXT_TOTAL_DENOMINATOR_URL = diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureBasisDef.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureBasisDef.java deleted file mode 100644 index 0a84c8b94..000000000 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureBasisDef.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.opencds.cqf.fhir.cr.measure.dstu3; - -import org.hl7.fhir.dstu3.model.Measure; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.opencds.cqf.fhir.cr.measure.common.MeasureBasisDef; -import org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants; - -/* - -*/ -public class Dstu3MeasureBasisDef implements MeasureBasisDef { - - @Override - public boolean isBooleanBasis(Measure measure) { - // check for population-basis Extension, assume boolean if no Extension is found - if (measure.hasExtension()) { - return measure.getExtension().stream().anyMatch(this::isBooleanBasisExtension); - } - return true; - } - - private boolean isBooleanBasisExtension(IBaseExtension item) { - return (item.getUrl().equalsIgnoreCase(MeasureConstants.POPULATION_BASIS_URL) - && item.getValue().toString().equalsIgnoreCase("boolean")); - } -} diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java index 39c0271b7..c075d3bd7 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java @@ -2,13 +2,14 @@ import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALDENOMINATOR; import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALNUMERATOR; +import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.FHIR_ALL_TYPES_SYSTEM_URL; import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_DECREASE; import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_INCREASE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; @@ -19,7 +20,6 @@ import org.hl7.fhir.dstu3.model.Measure.MeasureGroupStratifierComponent; import org.hl7.fhir.dstu3.model.Measure.MeasureSupplementalDataComponent; import org.hl7.fhir.dstu3.model.Resource; -import org.hl7.fhir.dstu3.model.StringType; import org.opencds.cqf.fhir.cr.measure.common.CodeDef; import org.opencds.cqf.fhir.cr.measure.common.ConceptDef; import org.opencds.cqf.fhir.cr.measure.common.GroupDef; @@ -30,6 +30,7 @@ import org.opencds.cqf.fhir.cr.measure.common.PopulationDef; import org.opencds.cqf.fhir.cr.measure.common.SdeDef; import org.opencds.cqf.fhir.cr.measure.common.StratifierDef; +import org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants; public class Dstu3MeasureDefBuilder implements MeasureDefBuilder { @Override @@ -47,20 +48,25 @@ public MeasureDef build(Measure measure) { sdes.add(sdeDef); } - // Groups - MeasureScoring groupMeasureScoringCode = getMeasureScoring(measure); + // scoring + MeasureScoring measureScoring = + MeasureScoring.fromCode(measure.getScoring().getCodingFirstRep().getCode()); + // populationBasis + var measureBasis = getMeasureBasis(measure); + // default value is true + // improvement Notation + var measureImpNotation = getMeasureImprovementNotation(measure); // The group size check here is to ensure that there's parity in the behavior of builder // between DSTU3 and R4. In R4, scoring can be on the group level so if we have an // empty measure we simply generate an empty MeasureReport. // This might not be the best behavior, but we want to ensure that the behavior is the same // between versions - if (!measure.getGroup().isEmpty() && groupMeasureScoringCode == null) { + if (measureScoring == null) { throw new IllegalArgumentException("MeasureScoring must be specified on Measure"); } List groups = new ArrayList<>(); for (MeasureGroupComponent group : measure.getGroup()) { - // Populations List populations = new ArrayList<>(); for (MeasureGroupPopulationComponent pop : group.getPopulation()) { @@ -101,22 +107,14 @@ public MeasureDef build(Measure measure) { null, // No code on group in dstu3 stratifiers, populations, - groupMeasureScoringCode, - isGroupIncreaseImprovementNotation(measure, group), - groupHasImprovementNotationExt(group)); + measureScoring, + false, // no group scoring + getImprovementNotation(measureImpNotation), + getPopulationBasisDef(measureBasis)); groups.add(groupDef); } - // define basis of measure - Dstu3MeasureBasisDef measureBasisDef = new Dstu3MeasureBasisDef(); - - return new MeasureDef( - measure.getId(), - measure.getUrl(), - measure.getVersion(), - groups, - sdes, - measureBasisDef.isBooleanBasis(measure), - useMeasureImprovementNotation(groups)); + + return new MeasureDef(measure.getId(), measure.getUrl(), measure.getVersion(), groups, sdes); } private PopulationDef checkPopulationForCode( @@ -163,58 +161,49 @@ private void checkId(Resource r) { } } - private MeasureScoring getMeasureScoring(Measure measure) { - return MeasureScoring.fromCode(measure.getScoring().getCodingFirstRep().getCode()); + private void validateImprovementNotationCode(CodeDef improvementNotation) { + if (improvementNotation != null) { + var code = improvementNotation.code(); + boolean hasValidCode = IMPROVEMENT_NOTATION_SYSTEM_INCREASE.equals(code) + || IMPROVEMENT_NOTATION_SYSTEM_DECREASE.equals(code); + if (!hasValidCode) { + throw new IllegalArgumentException(String.format( + "ImprovementNotation Coding has invalid code: %s, combination for Measure.", code)); + } + } } - private boolean isIncreaseImprovementNotation(String improvementNotationValue) { - validateImprovementNotationCode(improvementNotationValue); - return improvementNotationValue.equals(IMPROVEMENT_NOTATION_SYSTEM_INCREASE); - } + public CodeDef getMeasureBasis(Measure measure) { - private void validateImprovementNotationCode(String improvementNotationValue) { - boolean hasValidCode = IMPROVEMENT_NOTATION_SYSTEM_INCREASE.equals(improvementNotationValue) - || IMPROVEMENT_NOTATION_SYSTEM_DECREASE.equals(improvementNotationValue); - if (!hasValidCode) { - throw new IllegalArgumentException(String.format( - "ImprovementNotation Coding has invalid code: %s, combination for Measure.", - improvementNotationValue)); + var ext = measure.getExtensionByUrl(MeasureConstants.POPULATION_BASIS_URL); + // check for population-basis Extension, assume boolean if no Extension is found + if (ext != null) { + return new CodeDef( + MeasureConstants.FHIR_ALL_TYPES_SYSTEM_URL, ext.getValue().toString()); } + return null; } - public String getGroupImprovementNotationExt(Measure.MeasureGroupComponent group) { - var ext = group.getExtensionByUrl(MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION) - .getValue(); - assert ext instanceof StringType; - StringType value = (StringType) ext; - return value.getValue(); + public CodeDef getMeasureImprovementNotation(Measure measure) { + if (measure.hasImprovementNotation()) { + var impNot = new CodeDef(null, measure.getImprovementNotation()); + validateImprovementNotationCode(impNot); + // Dstu3 only has a string defined for improvementNotation + return impNot; + } + return null; } - private boolean groupHasImprovementNotationExt(MeasureGroupComponent group) { - return group.getExtensionByUrl(MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION) != null; + private boolean isBooleanBasis(CodeDef populationBasis) { + return populationBasis.code().equals("boolean"); } - private boolean isGroupIncreaseImprovementNotation(Measure measure, MeasureGroupComponent group) { - // default improvement Notation - boolean isIncreaseImpNotation = true; - boolean useGroupImpNotation = groupHasImprovementNotationExt(group); - if (useGroupImpNotation) { - isIncreaseImpNotation = isIncreaseImprovementNotation(getGroupImprovementNotationExt(group)); - } else if (measure.hasImprovementNotation()) { - isIncreaseImpNotation = isIncreaseImprovementNotation(measure.getImprovementNotation()); - } - - return isIncreaseImpNotation; + private CodeDef getPopulationBasisDef(CodeDef measureBasis) { + // default basis, if not defined + return Objects.requireNonNullElseGet(measureBasis, () -> new CodeDef(FHIR_ALL_TYPES_SYSTEM_URL, "boolean")); } - private boolean useMeasureImprovementNotation(List groups) { - // if no groups are present then useMeasure - if (groups == null || groups.isEmpty()) { - return true; - } else { - boolean useGroupImpNotation = groups.stream().allMatch(GroupDef::useGroupDefImprovementNotation) - && groups.get(0).useGroupDefImprovementNotation(); - return !useGroupImpNotation; - } + private CodeDef getImprovementNotation(CodeDef measureImpNotation) { + return Objects.requireNonNullElse(measureImpNotation, new CodeDef(null, IMPROVEMENT_NOTATION_SYSTEM_INCREASE)); } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilder.java index dfb9520a1..ff94c35fb 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilder.java @@ -286,7 +286,7 @@ protected void buildPopulation( reportPopulation.setCode(measurePopulation.getCode()); reportPopulation.setId(measurePopulation.getId()); - if (measureDef.isBooleanBasis()) { + if (measureDef.groups().get(0).isBooleanBasis()) { reportPopulation.setCount(populationDef.getResources().size()); } else { reportPopulation.setCount(populationDef.getSubjects().size()); diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsProcessor.java index 9509d9536..64a06bf96 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsProcessor.java @@ -21,6 +21,7 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cr.measure.CareGapsProperties; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; +import org.opencds.cqf.fhir.cr.measure.common.GroupDef; import org.opencds.cqf.fhir.cr.measure.common.MeasurePeriodValidator; import org.opencds.cqf.fhir.cr.measure.common.MeasureScoring; import org.opencds.cqf.fhir.cr.measure.constant.CareGapsConstants; @@ -187,10 +188,14 @@ protected void measureCompatibilityCheck(List measures) { } protected void checkMeasureBasis(Measure measure) { - R4MeasureBasisDef measureDef = new R4MeasureBasisDef(); - if (!measureDef.isBooleanBasis(measure)) { - throw new IllegalArgumentException( - String.format("CareGaps can't process Measure: %s, it is not Boolean basis.", measure.getIdPart())); + var msg = String.format("CareGaps can't process Measure: %s, it is not Boolean basis.", measure.getIdPart()); + R4MeasureDefBuilder measureDefBuilder = new R4MeasureDefBuilder(); + var measureDef = measureDefBuilder.build(measure); + + for (GroupDef groupDef : measureDef.groups()) { + if (!groupDef.isBooleanBasis()) { + throw new IllegalArgumentException(msg); + } } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureBasisDef.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureBasisDef.java deleted file mode 100644 index 98795d184..000000000 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureBasisDef.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.opencds.cqf.fhir.cr.measure.r4; - -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.r4.model.Measure; -import org.opencds.cqf.fhir.cr.measure.common.MeasureBasisDef; -import org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants; - -/* - -*/ -public class R4MeasureBasisDef implements MeasureBasisDef { - - @Override - public boolean isBooleanBasis(Measure measure) { - // check for population-basis Extension, assume boolean if no Extension is found - if (measure.hasExtension()) { - return measure.getExtension().stream().anyMatch(this::isBooleanBasisExtension); - } - return true; - } - - private boolean isBooleanBasisExtension(IBaseExtension item) { - return (item.getUrl().equalsIgnoreCase(MeasureConstants.POPULATION_BASIS_URL) - && item.getValue().toString().equalsIgnoreCase("boolean")); - } -} diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java index bb80a0712..f743779b7 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java @@ -5,6 +5,7 @@ import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALNUMERATOR; import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.CQFM_CARE_GAP_DATE_OF_COMPLIANCE_EXT_URL; import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.CQFM_SCORING_EXT_URL; +import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.FHIR_ALL_TYPES_SYSTEM_URL; import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_DECREASE; import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_INCREASE; import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION; @@ -14,12 +15,15 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Element; +import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Expression; +import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.Measure; import org.hl7.fhir.r4.model.Measure.MeasureGroupComponent; import org.hl7.fhir.r4.model.Measure.MeasureGroupPopulationComponent; @@ -38,6 +42,7 @@ import org.opencds.cqf.fhir.cr.measure.common.SdeDef; import org.opencds.cqf.fhir.cr.measure.common.StratifierComponentDef; import org.opencds.cqf.fhir.cr.measure.common.StratifierDef; +import org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants; public class R4MeasureDefBuilder implements MeasureDefBuilder { @Override @@ -53,17 +58,23 @@ public MeasureDef build(Measure measure) { s.getId(), conceptToConceptDef(s.getCode()), s.getCriteria().getExpression()); sdes.add(sdeDef); } + // scoring + var measureScoring = getMeasureScoring(measure); + // populationBasis + var measureBasis = getMeasureBasis(measure); + // improvement Notation + var measureImpNotation = getMeasureImprovementNotation(measure); // Groups - var measureLevelMeasureScoring = getMeasureScoring(measure); List groups = new ArrayList<>(); for (MeasureGroupComponent group : measure.getGroup()) { // group Measure Scoring - var groupMeasureScoringCode = getGroupMeasureScoring(measureLevelMeasureScoring, group); - - if (groupMeasureScoringCode == null) { - throw new IllegalArgumentException("MeasureScoring must be specified on Group or Measure"); - } + var groupScoring = getGroupMeasureScoring(group); + // populationBasis + var groupBasis = getGroupPopulationBasis(group); + // improvement Notation + var groupImpNotation = getGroupImpNotation(group); + var hasGroupImpNotation = groupImpNotation != null; // Populations List populations = new ArrayList<>(); @@ -135,32 +146,13 @@ && checkPopulationForCode(populations, DATEOFCOMPLIANCE) == null) { conceptToConceptDef(group.getCode()), stratifiers, populations, - groupMeasureScoringCode, - isGroupIncreaseImprovementNotation(measure, group), - groupHasImprovementNotationExt(group)); + getScoringDef(measureScoring, groupScoring), + hasGroupImpNotation, + getImprovementNotation(measureImpNotation, groupImpNotation), + getPopulationBasisDef(measureBasis, groupBasis)); groups.add(groupDef); } - // define basis of measure - R4MeasureBasisDef measureBasisDef = new R4MeasureBasisDef(); - return new MeasureDef( - measure.getId(), - measure.getUrl(), - measure.getVersion(), - groups, - sdes, - measureBasisDef.isBooleanBasis(measure), - useMeasureImprovementNotation(groups)); - } - - public boolean useMeasureImprovementNotation(List groups) { - // if no groups are present then useMeasure - if (groups == null || groups.isEmpty()) { - return true; - } else { - boolean useGroupImpNotation = groups.stream().allMatch(GroupDef::useGroupDefImprovementNotation) - && groups.get(0).useGroupDefImprovementNotation(); - return !useGroupImpNotation; - } + return new MeasureDef(measure.getId(), measure.getUrl(), measure.getVersion(), groups, sdes); } private PopulationDef checkPopulationForCode( @@ -217,8 +209,7 @@ private void checkId(Resource r) { } } - private MeasureScoring getMeasureScoring(Measure measure) { - var scoringCode = measure.getScoring().getCodingFirstRep().getCode(); + private MeasureScoring getMeasureScoring(String scoringCode) { if (scoringCode != null) { var code = MeasureScoring.fromCode(scoringCode); if (code == null) { @@ -231,27 +222,14 @@ private MeasureScoring getMeasureScoring(Measure measure) { return null; } - private MeasureScoring getGroupMeasureScoring(MeasureScoring measureLevelScoring, MeasureGroupComponent group) { - // see if group component has scoring Url - var scoringExtension = group.getExtensionByUrl(CQFM_SCORING_EXT_URL); - if (scoringExtension != null) { - // extract scoringType - CodeableConcept coding = (CodeableConcept) scoringExtension.getValue(); - return MeasureScoring.fromCode(coding.getCodingFirstRep().getCode()); - } - // otherwise return measureLevelScoring - return measureLevelScoring; - } - - private boolean isIncreaseImprovementNotation(CodeableConcept improvementNotationValue) { - validateImprovementNotationCode(improvementNotationValue); - return improvementNotationValue.hasCoding( - MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, IMPROVEMENT_NOTATION_SYSTEM_INCREASE); + private MeasureScoring getMeasureScoring(Measure measure) { + var scoringCode = measure.getScoring().getCodingFirstRep().getCode(); + return getMeasureScoring(scoringCode); } - private void validateImprovementNotationCode(CodeableConcept improvementNotationValue) { - var code = improvementNotationValue.getCodingFirstRep().getCode(); - var system = improvementNotationValue.getCodingFirstRep().getSystem(); + private void validateImprovementNotationCode(CodeDef improvementNotation) { + var code = improvementNotation.code(); + var system = improvementNotation.system(); boolean hasValidSystem = system.equals(MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM); boolean hasValidCode = IMPROVEMENT_NOTATION_SYSTEM_INCREASE.equals(code) || IMPROVEMENT_NOTATION_SYSTEM_DECREASE.equals(code); @@ -262,27 +240,87 @@ private void validateImprovementNotationCode(CodeableConcept improvementNotation } } - private CodeableConcept getGroupImprovementNotationExt(MeasureGroupComponent group) { - var ext = group.getExtensionByUrl(MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION) - .getValue(); - assert ext instanceof CodeableConcept; - return (CodeableConcept) ext; + public CodeDef getMeasureBasis(Measure measure) { + + var ext = measure.getExtensionByUrl(MeasureConstants.POPULATION_BASIS_URL); + // check for population-basis Extension, assume boolean if no Extension is found + if (ext != null) { + return makeCodeDefFromExtension(ext); + } + return null; + } + + private CodeDef makeCodeDefFromExtension(Extension extension) { + var code = extension.getValue().toString(); + // validate code membership + assert Enumerations.FHIRAllTypes.fromCode(code) != null; + return new CodeDef(MeasureConstants.POPULATION_BASIS_URL, code); + } + + public CodeDef getMeasureImprovementNotation(Measure measure) { + if (measure.hasImprovementNotation()) { + var improvementNotationValue = measure.getImprovementNotation(); + var codeDef = new CodeDef( + improvementNotationValue.getCodingFirstRep().getSystem(), + improvementNotationValue.getCodingFirstRep().getCode()); + validateImprovementNotationCode(codeDef); + return codeDef; + } + return null; + } + + public CodeDef getGroupImpNotation(MeasureGroupComponent group) { + var ext = group.getExtensionByUrl(MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION); + if (ext != null) { + var value = ext.getValue(); + assert value instanceof CodeableConcept; + CodeableConcept coding = (CodeableConcept) value; + var codeDef = new CodeDef( + coding.getCodingFirstRep().getSystem(), + coding.getCodingFirstRep().getCode()); + validateImprovementNotationCode(codeDef); + return codeDef; + } + return null; + } + + public MeasureScoring getGroupMeasureScoring(MeasureGroupComponent group) { + var ext = group.getExtensionByUrl(CQFM_SCORING_EXT_URL); + if (ext != null) { + return getMeasureScoring(ext.getValue().toString()); + } + return null; + } + + public CodeDef getGroupPopulationBasis(MeasureGroupComponent group) { + var ext = group.getExtensionByUrl(MeasureConstants.POPULATION_BASIS_URL); + // check for population-basis Extension, assume boolean if no Extension is found + if (ext != null) { + return makeCodeDefFromExtension(ext); + } + return null; } - private boolean groupHasImprovementNotationExt(MeasureGroupComponent group) { - return group.getExtensionByUrl(MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION) != null; + private MeasureScoring getScoringDef(MeasureScoring measureScoring, MeasureScoring groupScoring) { + if (groupScoring == null && measureScoring == null) { + throw new IllegalArgumentException("MeasureScoring must be specified on Group or Measure"); + } + return Objects.requireNonNullElse(groupScoring, measureScoring); } - private boolean isGroupIncreaseImprovementNotation(Measure measure, MeasureGroupComponent group) { - // default improvement Notation - boolean isIncreaseImpNotation = true; - boolean useGroupImpNotation = groupHasImprovementNotationExt(group); - if (useGroupImpNotation) { - isIncreaseImpNotation = isIncreaseImprovementNotation(getGroupImprovementNotationExt(group)); - } else if (measure.hasImprovementNotation()) { - isIncreaseImpNotation = isIncreaseImprovementNotation(measure.getImprovementNotation()); + private CodeDef getPopulationBasisDef(CodeDef measureBasis, CodeDef groupBasis) { + if (measureBasis == null && groupBasis == null) { + // default basis, if not defined + return new CodeDef(FHIR_ALL_TYPES_SYSTEM_URL, "boolean"); } + return Objects.requireNonNullElse(groupBasis, measureBasis); + } - return isIncreaseImpNotation; + private CodeDef getImprovementNotation(CodeDef measureImpNotation, CodeDef groupImpNotation) { + if (measureImpNotation == null && groupImpNotation == null) { + // default Improvement Notation, if not defined + return new CodeDef(MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, IMPROVEMENT_NOTATION_SYSTEM_INCREASE); + } + return Objects.requireNonNullElse(groupImpNotation, measureImpNotation); } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilder.java index 85c51474a..92d5dd427 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilder.java @@ -301,7 +301,7 @@ protected void buildGroup( reportGroup.setId(measureGroup.getId()); // Measure Level Extension addMeasureDescription(reportGroup, measureGroup); - addExtensionImprovementNotation(reportGroup, bc.measureDef, groupDef); + addExtensionImprovementNotation(reportGroup, groupDef); for (int i = 0; i < measureGroup.getPopulation().size(); i++) { var measurePop = measureGroup.getPopulation().get(i); @@ -318,7 +318,7 @@ protected void buildGroup( } } var reportPop = reportGroup.addPopulation(); - buildPopulation(bc, measurePop, reportPop, defPop); + buildPopulation(bc, measurePop, reportPop, defPop, groupDef); } // add extension to group for totalDenominator and totalNumerator @@ -345,32 +345,16 @@ protected void buildGroup( } } - if (bc.measureDef.isBooleanBasis()) { - reportGroup - .addExtension() - .setUrl(EXT_TOTAL_DENOMINATOR_URL) - .setValue(new StringType(Integer.toString(getReportPopulation(groupDef, TOTALDENOMINATOR) - .getSubjects() - .size()))); - reportGroup - .addExtension() - .setUrl(EXT_TOTAL_NUMERATOR_URL) - .setValue(new StringType(Integer.toString(getReportPopulation(groupDef, TOTALNUMERATOR) - .getSubjects() - .size()))); + if (groupDef.isBooleanBasis()) { + addExtension( + reportGroup, EXT_TOTAL_DENOMINATOR_URL, getReportPopulation(groupDef, TOTALDENOMINATOR), true); + addExtension(reportGroup, EXT_TOTAL_NUMERATOR_URL, getReportPopulation(groupDef, TOTALNUMERATOR), true); + } else { - reportGroup - .addExtension() - .setUrl(EXT_TOTAL_DENOMINATOR_URL) - .setValue(new StringType(Integer.toString(getReportPopulation(groupDef, TOTALDENOMINATOR) - .getResources() - .size()))); - reportGroup - .addExtension() - .setUrl(EXT_TOTAL_NUMERATOR_URL) - .setValue(new StringType(Integer.toString(getReportPopulation(groupDef, TOTALNUMERATOR) - .getResources() - .size()))); + addExtension( + reportGroup, EXT_TOTAL_DENOMINATOR_URL, getReportPopulation(groupDef, TOTALDENOMINATOR), false); + addExtension( + reportGroup, EXT_TOTAL_NUMERATOR_URL, getReportPopulation(groupDef, TOTALNUMERATOR), false); } } for (int i = 0; i < measureGroup.getStratifier().size(); i++) { @@ -381,6 +365,18 @@ protected void buildGroup( } } + protected void addExtension( + MeasureReportGroupComponent group, String extUrl, PopulationDef populationDef, boolean useSubjects) { + int count; + if (useSubjects) { + count = populationDef.getSubjects().size(); + } else { + count = populationDef.getResources().size(); + } + + group.addExtension().setUrl(extUrl).setValue(new StringType(Integer.toString(count))); + } + /** * * Resource result --> Patient Key, Resource result --> can intersect on patient for Boolean basis, can't for Resource @@ -418,10 +414,10 @@ protected void buildStratifier( Map subjectValues = stratifierDef.getResults(); - validateStratifierBasisType(subjectValues, bc.measureDef.isBooleanBasis()); + validateStratifierBasisType(subjectValues, groupDef.isBooleanBasis()); // Stratifiers should be of the same basis as population - if (bc.measureDef.isBooleanBasis()) { + if (groupDef.isBooleanBasis()) { // ValueWrapper is used because most of the types we're dealing with don't implement hashCode or equals Map> subjectsByValue = subjectValues.keySet().stream() .collect(Collectors.groupingBy( @@ -460,10 +456,9 @@ protected void addMeasureDescription(MeasureReportGroupComponent reportGroup, Me } } - protected void addExtensionImprovementNotation( - MeasureReportGroupComponent reportGroup, MeasureDef measureDef, GroupDef groupDef) { + protected void addExtensionImprovementNotation(MeasureReportGroupComponent reportGroup, GroupDef groupDef) { // if already set on Measure, don't set on groups too - if (!measureDef.useMeasureImpNotation()) { + if (groupDef.isGroupImprovementNotation()) { if (groupDef.isIncreaseImprovementNotation()) { reportGroup.addExtension( MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION, @@ -502,15 +497,8 @@ protected void buildStratum( } // add totalDenominator and totalNumerator extensions - buildStratumExtPopulation( - groupDef, - TOTALDENOMINATOR, - subjectIds, - stratum, - EXT_TOTAL_DENOMINATOR_URL, - bc.measureDef.isBooleanBasis()); - buildStratumExtPopulation( - groupDef, TOTALNUMERATOR, subjectIds, stratum, EXT_TOTAL_NUMERATOR_URL, bc.measureDef.isBooleanBasis()); + buildStratumExtPopulation(groupDef, TOTALDENOMINATOR, subjectIds, stratum, EXT_TOTAL_DENOMINATOR_URL); + buildStratumExtPopulation(groupDef, TOTALNUMERATOR, subjectIds, stratum, EXT_TOTAL_NUMERATOR_URL); } protected void buildStratumExtPopulation( @@ -518,12 +506,11 @@ protected void buildStratumExtPopulation( MeasurePopulationType measurePopulationType, List subjectIds, StratifierGroupComponent stratum, - String extUrl, - boolean isBooleanBasis) { + String extUrl) { Set subjectPop; var reportPopulation = getReportPopulation(groupDef, measurePopulationType); assert reportPopulation != null; - if (isBooleanBasis) { + if (groupDef.isBooleanBasis()) { subjectPop = reportPopulation.getSubjects().stream() .map(t -> ResourceType.Patient.toString().concat("/").concat(t)) .collect(Collectors.toSet()); @@ -585,12 +572,13 @@ protected void buildPopulation( BuilderContext bc, MeasureGroupPopulationComponent measurePopulation, MeasureReportGroupPopulationComponent reportPopulation, - PopulationDef populationDef) { + PopulationDef populationDef, + GroupDef groupDef) { reportPopulation.setCode(measurePopulation.getCode()); reportPopulation.setId(measurePopulation.getId()); - if (bc.measureDef.isBooleanBasis()) { + if (groupDef.isBooleanBasis()) { reportPopulation.setCount(populationDef.getSubjects().size()); } else { reportPopulation.setCount(populationDef.getResources().size()); @@ -607,7 +595,7 @@ protected void buildPopulation( // This is a temporary list carried forward to stratifiers // subjectResult set defined by basis of Measure Set populationSet; - if (bc.measureDef.isBooleanBasis()) { + if (groupDef.isBooleanBasis()) { populationSet = populationDef.getSubjects().stream() .map(t -> ResourceType.Patient.toString().concat("/").concat(t)) .collect(Collectors.toSet()); @@ -792,7 +780,7 @@ protected MeasureReport createMeasureReport( report.setMeasure(getMeasure(measure)); report.setDate(new java.util.Date()); report.setImplicitRules(measure.getImplicitRules()); - if (measureDef.useMeasureImpNotation()) { + if (!measureDef.groups().get(0).isGroupImprovementNotation()) { // if true, all group components have the same improvement Notation report.setImprovementNotation(measure.getImprovementNotation()); } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilderTest.java index edaca82e1..12d88c5e0 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilderTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilderTest.java @@ -1,5 +1,6 @@ package org.opencds.cqf.fhir.cr.measure.dstu3; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -13,6 +14,7 @@ import org.hl7.fhir.dstu3.model.StringType; import org.junit.jupiter.api.Test; import org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType; +import org.opencds.cqf.fhir.cr.measure.common.MeasureScoring; class Dstu3MeasureDefBuilderTest { @@ -21,6 +23,8 @@ class Dstu3MeasureDefBuilderTest { new Extension().setUrl(POPULATION_BASIS_URL).setValue(new StringType("boolean")); private static final Extension RESOURCE_BASIS_EXT = new Extension().setUrl(POPULATION_BASIS_URL).setValue(new StringType("encounter")); + private static final String INCREASE = "increase"; + private static final String DECREASE = "decrease"; @Test void defBuilderThrowsWhenMeasureIdMissing() { @@ -50,7 +54,7 @@ void defBuilderBooleanBasis_WithResourceExt() { measure.setExtension(Collections.singletonList(RESOURCE_BASIS_EXT)); var def = BUILDER.build(measure); - assertFalse(def.isBooleanBasis()); + assertFalse(def.groups().get(0).isBooleanBasis()); } @Test @@ -64,7 +68,7 @@ void defBuilderBooleanBasis_WithBooleanExt() { measure.setExtension(Collections.singletonList(BOOLEAN_BASIS_EXT)); var def = BUILDER.build(measure); - assertTrue(def.isBooleanBasis()); + assertTrue(def.groups().get(0).isBooleanBasis()); } @Test @@ -77,6 +81,68 @@ void defBuilderBooleanBasis_NoBasisExt() { grp.getCode().getCodingFirstRep().setCode(MeasurePopulationType.INITIALPOPULATION.toCode()); // no extension set to define basis of Measure var def = BUILDER.build(measure); - assertTrue(def.isBooleanBasis()); + assertTrue(def.groups().get(0).isBooleanBasis()); + } + + @Test + void defBuilderImpNotationIncrease() { + var measure = new Measure(); + measure.setId("123"); + measure.setImprovementNotation(INCREASE); + measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode("proportion"))); + var grp = measure.addGroup().addPopulation(); + grp.setId("pop-id"); + grp.getCode().getCodingFirstRep().setCode(MeasurePopulationType.INITIALPOPULATION.toCode()); + // no extension set to define basis of Measure + var def = BUILDER.build(measure); + assertFalse(def.groups().get(0).isGroupImprovementNotation()); + assertEquals(INCREASE, def.groups().get(0).getImprovementNotation().code()); + assertTrue(def.groups().get(0).isIncreaseImprovementNotation()); + } + + @Test + void defBuilderImpNotationDecrease() { + var measure = new Measure(); + measure.setId("123"); + measure.setImprovementNotation(DECREASE); + measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode("proportion"))); + var grp = measure.addGroup().addPopulation(); + grp.setId("pop-id"); + grp.getCode().getCodingFirstRep().setCode(MeasurePopulationType.INITIALPOPULATION.toCode()); + // no extension set to define basis of Measure + var def = BUILDER.build(measure); + assertFalse(def.groups().get(0).isGroupImprovementNotation()); + assertEquals(DECREASE, def.groups().get(0).getImprovementNotation().code()); + assertFalse(def.groups().get(0).isIncreaseImprovementNotation()); + } + + @Test + void defBuilderImpNotationEmpty() { + var measure = new Measure(); + measure.setId("123"); + measure.setImprovementNotation(INCREASE); + measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode("proportion"))); + var grp = measure.addGroup().addPopulation(); + grp.setId("pop-id"); + grp.getCode().getCodingFirstRep().setCode(MeasurePopulationType.INITIALPOPULATION.toCode()); + // no extension set to define basis of Measure + var def = BUILDER.build(measure); + assertFalse(def.groups().get(0).isGroupImprovementNotation()); + assertEquals(INCREASE, def.groups().get(0).getImprovementNotation().code()); + assertTrue(def.groups().get(0).isIncreaseImprovementNotation()); + } + + @Test + void defBuilderMeasureScoring() { + var measure = new Measure(); + measure.setId("123"); + measure.setImprovementNotation(INCREASE); + measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode("proportion"))); + var grp = measure.addGroup().addPopulation(); + grp.setId("pop-id"); + grp.getCode().getCodingFirstRep().setCode(MeasurePopulationType.INITIALPOPULATION.toCode()); + // no extension set to define basis of Measure + var def = BUILDER.build(measure); + assertEquals(MeasureScoring.PROPORTION, def.groups().get(0).measureScoring()); } } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilderTest.java index 073d26634..aeb405330 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilderTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilderTest.java @@ -1,9 +1,5 @@ package org.opencds.cqf.fhir.cr.measure.dstu3; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.parser.IParser; @@ -27,24 +23,30 @@ void setup() { @Test void checkIfNotBooleanBasedMeasure() { + Dstu3MeasureDefBuilder defBuilder = new Dstu3MeasureDefBuilder(); IParser parser = fhirContext.newJsonParser(); + Measure measureEncounterBias = (Measure) parser.parseResource(Dstu3MeasureReportBuilderTest.class.getResourceAsStream("EXM105FHIR3Sample.json")); + var measureEBDef = defBuilder.build(measureEncounterBias); + Measure measureWithoutExtension = (Measure) parser.parseResource( Dstu3MeasureReportBuilderTest.class.getResourceAsStream("EXM105FHIR3SampleWithoutExtension.json")); + var measureWEDef = defBuilder.build(measureWithoutExtension); + Measure measureBooleanBias = (Measure) parser.parseResource(Dstu3MeasureReportBuilderTest.class.getResourceAsStream( "EXM105FHIR3SampleWithBoolenPopulationBias.json")); - Dstu3MeasureBasisDef measureBasisDef = new Dstu3MeasureBasisDef(); + var measureBBDef = defBuilder.build(measureBooleanBias); - assertNotNull(measureEncounterBias); // Encounter Basis - assertFalse(measureBasisDef.isBooleanBasis(measureEncounterBias)); + /* assertNotNull(measureEncounterBias); + assertFalse(measureEBDef.isBooleanBasis()); // No specified Basis assertNotNull(measureWithoutExtension); - assertTrue(measureBasisDef.isBooleanBasis(measureWithoutExtension)); + assertTrue(measureWEDef.isBooleanBasis()); // Boolean Basis assertNotNull(measureBooleanBias); - assertTrue(measureBasisDef.isBooleanBasis(measureBooleanBias)); + assertTrue(measureBBDef.isBooleanBasis());*/ } } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java new file mode 100644 index 000000000..b32dc7d4e --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java @@ -0,0 +1,450 @@ +package org.opencds.cqf.fhir.cr.measure.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_DECREASE; +import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_INCREASE; +import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.parser.IParser; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.StringType; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.cr.measure.common.MeasureDef; +import org.opencds.cqf.fhir.cr.measure.common.MeasureScoring; +import org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants; +import org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants; + +public class MeasureDefBuilderTest { + // resource basis + // boolean basis + // group defined basis --> error + // no basis + private final CodeableConcept increase = new CodeableConcept(new Coding( + MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, + IMPROVEMENT_NOTATION_SYSTEM_INCREASE, + MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_INCREASE_DISPLAY)); + + private final CodeableConcept decrease = new CodeableConcept(new Coding( + MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, + IMPROVEMENT_NOTATION_SYSTEM_DECREASE, + MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_DECREASE_DISPLAY)); + private final CodeableConcept invalid = new CodeableConcept( + new Coding(MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, "fake", "Fake")); + private final FhirContext fhirContext = FhirContext.forCached(FhirVersionEnum.R4); + private final IParser parser = fhirContext.newJsonParser(); + + public MeasureDef measureDefBuilder( + String group1Basis, + String group1Scoring, + CodeableConcept group1ImpNotation, + String group2Basis, + String group2Scoring, + CodeableConcept group2ImpNotation, + String measureBasis, + String measureScoring, + CodeableConcept measureImpNotation) { + + R4MeasureDefBuilder defBuilder = new R4MeasureDefBuilder(); + org.hl7.fhir.r4.model.Measure measure = (org.hl7.fhir.r4.model.Measure) + parser.parseResource(MeasureDefBuilderTest.class.getResourceAsStream("TemplateMeasure.json")); + + var group1 = measure.getGroup().stream() + .filter(t -> t.getId().equals("group-1")) + .findFirst() + .get(); + if (group1Basis != null) { + group1.addExtension(new Extension() + .setUrl(MeasureConstants.POPULATION_BASIS_URL) + .setValue(new CodeType(group1Basis))); + } + if (group1Scoring != null) { + group1.addExtension(new Extension() + .setUrl(MeasureConstants.CQFM_SCORING_EXT_URL) + .setValue(new StringType(group1Scoring))); + } + if (group1ImpNotation != null) { + group1.addExtension(new Extension() + .setUrl(MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION) + .setValue(group1ImpNotation)); + } + var group2 = measure.getGroup().stream() + .filter(t -> t.getId().equals("group-2")) + .findFirst() + .get(); + if (group2Basis != null) { + group2.addExtension(new Extension() + .setUrl(MeasureConstants.POPULATION_BASIS_URL) + .setValue(new CodeType(group2Basis))); + } + if (group2Scoring != null) { + group2.addExtension(new Extension() + .setUrl(MeasureConstants.CQFM_SCORING_EXT_URL) + .setValue(new StringType(group2Scoring))); + } + if (group2ImpNotation != null) { + group2.addExtension(new Extension() + .setUrl(MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION) + .setValue(group2ImpNotation)); + } + if (measureScoring != null) { + measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode(measureScoring))); + } + if (measureImpNotation != null) { + measure.setImprovementNotation(measureImpNotation); + } + if (measureBasis != null) { + measure.addExtension(new Extension() + .setUrl(MeasureConstants.POPULATION_BASIS_URL) + .setValue(new CodeType(measureBasis))); + } + return defBuilder.build(measure); + } + + public void validateMeasureDef( + MeasureDef measureDef, + boolean group1IsBooleanBasis, + String group1Basis, + boolean group1IsGroupImpNotation, + String group1ImpNotationValue, + MeasureScoring group1MeasureScoring, + boolean group2IsBooleanBasis, + String group2Basis, + boolean group2IsGroupImpNotation, + String group2ImpNotationValue, + MeasureScoring group2MeasureScoring) { + + var group1 = measureDef.groups().stream() + .filter(t -> t.id().equals("group-1")) + .findFirst() + .orElse(null); + // Basis + assertEquals(group1IsBooleanBasis, group1.isBooleanBasis()); + assertEquals(group1Basis, group1.getPopulationBasis().code()); + // Improvement Notation + assertEquals(group1IsGroupImpNotation, group1.isGroupImprovementNotation()); + assertEquals(group1ImpNotationValue, group1.getImprovementNotation().code()); + // Scoring + assertEquals(group1MeasureScoring, group1.measureScoring()); + + var group2 = measureDef.groups().stream() + .filter(t -> t.id().equals("group-2")) + .findFirst() + .orElse(null); + // Basis + assertEquals(group2IsBooleanBasis, group2.isBooleanBasis()); + assertEquals(group2Basis, group2.getPopulationBasis().code()); + // Improvement Notation + assertEquals(group2IsGroupImpNotation, group2.isGroupImprovementNotation()); + assertEquals(group2ImpNotationValue, group2.getImprovementNotation().code()); + // Scoring + assertEquals(group2MeasureScoring, group2.measureScoring()); + } + + @Test + void basisMeasure() { + var def = measureDefBuilder(null, null, null, null, null, null, "boolean", "ratio", decrease); + + validateMeasureDef( + def, + true, + "boolean", + false, + "decrease", + MeasureScoring.RATIO, + true, + "boolean", + false, + "decrease", + MeasureScoring.RATIO); + } + + @Test + void basisMeasureAndGroup() { + var def = measureDefBuilder( + "Encounter", "cohort", increase, "Encounter", "cohort", increase, "boolean", "ratio", decrease); + + validateMeasureDef( + def, + false, + "Encounter", + true, + "increase", + MeasureScoring.COHORT, + false, + "Encounter", + true, + "increase", + MeasureScoring.COHORT); + } + + @Test + void basisOnlyGroup() { + var def = measureDefBuilder("Encounter", "cohort", increase, "Encounter", "cohort", increase, null, null, null); + + validateMeasureDef( + def, + false, + "Encounter", + true, + "increase", + MeasureScoring.COHORT, + false, + "Encounter", + true, + "increase", + MeasureScoring.COHORT); + } + + @Test + void basisDifferentGroup() { + var def = measureDefBuilder("Encounter", "cohort", decrease, "boolean", "cohort", increase, null, null, null); + + validateMeasureDef( + def, + false, + "Encounter", + true, + "decrease", + MeasureScoring.COHORT, + true, + "boolean", + true, + "increase", + MeasureScoring.COHORT); + } + + @Test + void basisNotDefined() { + var def = measureDefBuilder(null, "cohort", decrease, null, "cohort", increase, null, null, null); + + validateMeasureDef( + def, + true, + "boolean", + true, + "decrease", + MeasureScoring.COHORT, + true, + "boolean", + true, + "increase", + MeasureScoring.COHORT); + } + + @Test + void basisPartiallyDefinedGroup() { + var def = measureDefBuilder(null, "cohort", decrease, "Encounter", "cohort", increase, null, null, null); + + validateMeasureDef( + def, + true, + "boolean", + true, + "decrease", + MeasureScoring.COHORT, + false, + "Encounter", + true, + "increase", + MeasureScoring.COHORT); + } + + @Test + void basisInvalidGroup() { + try { + var def = measureDefBuilder("fish", "cohort", decrease, "fish", "cohort", increase, null, null, null); + fail("invalid code, should fail"); + } catch (FHIRException e) { + assertTrue(e.getMessage().contains("Unknown FHIRAllTypes code 'fish'")); + } + } + + @Test + void BasisMeasureInvalid() { + try { + var def = measureDefBuilder(null, null, null, null, null, null, "whale", "ratio", decrease); + fail("invalid code, should fail"); + } catch (FHIRException e) { + assertTrue(e.getMessage().contains("Unknown FHIRAllTypes code 'whale'")); + } + } + + @Test + void scoringMeasureScoringAndGroup() { + var def = measureDefBuilder(null, "ratio", null, null, "proportion", null, "boolean", "cohort", decrease); + + validateMeasureDef( + def, + true, + "boolean", + false, + "decrease", + MeasureScoring.RATIO, + true, + "boolean", + false, + "decrease", + MeasureScoring.PROPORTION); + } + + @Test + void scoringMeasure() { + var def = measureDefBuilder(null, null, null, null, null, null, "boolean", "cohort", decrease); + + validateMeasureDef( + def, + true, + "boolean", + false, + "decrease", + MeasureScoring.COHORT, + true, + "boolean", + false, + "decrease", + MeasureScoring.COHORT); + } + + @Test + void groupScoring() { + var def = measureDefBuilder(null, "ratio", null, null, "proportion", null, "boolean", null, decrease); + + validateMeasureDef( + def, + true, + "boolean", + false, + "decrease", + MeasureScoring.RATIO, + true, + "boolean", + false, + "decrease", + MeasureScoring.PROPORTION); + } + + @Test + void noScoring() { + try { + var def = measureDefBuilder(null, null, null, null, null, null, "boolean", null, null); + fail("measureScoring needs to be defined"); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("MeasureScoring must be specified on Group or Measure")); + } + } + + @Test + void invalidMeasureScoring() { + try { + var def = measureDefBuilder(null, null, null, null, null, null, "boolean", "attestation", null); + fail("measureScoring needs to be defined"); + } catch (IllegalArgumentException e) { + assertTrue( + e.getMessage().contains("Measure Scoring code: attestation, is not a valid Measure Scoring Type.")); + } + } + + @Test + void invalidGroupScoring() { + try { + var def = measureDefBuilder(null, "attestation", null, null, "attestation", null, "boolean", null, null); + fail("measureScoring needs to be defined"); + } catch (IllegalArgumentException e) { + assertTrue( + e.getMessage().contains("Measure Scoring code: attestation, is not a valid Measure Scoring Type.")); + } + } + + @Test + void groupAndMeasureImprovementNotation() { + var def = measureDefBuilder(null, "ratio", increase, null, "proportion", increase, "boolean", null, decrease); + + validateMeasureDef( + def, + true, + "boolean", + true, + "increase", + MeasureScoring.RATIO, + true, + "boolean", + true, + "increase", + MeasureScoring.PROPORTION); + } + + @Test + void groupImprovementNotation() { + var def = measureDefBuilder(null, "ratio", increase, null, "proportion", increase, "boolean", null, null); + + validateMeasureDef( + def, + true, + "boolean", + true, + "increase", + MeasureScoring.RATIO, + true, + "boolean", + true, + "increase", + MeasureScoring.PROPORTION); + } + + @Test + void measureImprovementNotation() { + var def = measureDefBuilder(null, "ratio", null, null, "proportion", null, "boolean", null, decrease); + + validateMeasureDef( + def, + true, + "boolean", + false, + "decrease", + MeasureScoring.RATIO, + true, + "boolean", + false, + "decrease", + MeasureScoring.PROPORTION); + } + + @Test + void noImprovementNotation() { + var def = measureDefBuilder(null, "ratio", null, null, "proportion", null, "boolean", null, null); + + validateMeasureDef( + def, + true, + "boolean", + false, + "increase", + MeasureScoring.RATIO, + true, + "boolean", + false, + "increase", + MeasureScoring.PROPORTION); + } + + @Test + void invalidImprovementNotation() { + try { + var def = measureDefBuilder(null, "ratio", invalid, null, "proportion", invalid, "boolean", null, null); + fail("invalid improvement Notation value"); + } catch (IllegalArgumentException e) { + assertTrue( + e.getMessage() + .contains( + "ImprovementNotation Coding has invalid System: http://terminology.hl7.org/CodeSystem/measure-improvement-notation, code: fake, combination for Measure.")); + } + } +} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilderTest.java deleted file mode 100644 index 65099ee2d..000000000 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilderTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.opencds.cqf.fhir.cr.measure.r4; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.POPULATION_BASIS_URL; - -import java.util.Collections; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.Measure; -import org.hl7.fhir.r4.model.StringType; -import org.junit.jupiter.api.Test; -import org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType; - -class R4MeasureDefBuilderTest { - - private static final R4MeasureDefBuilder BUILDER = new R4MeasureDefBuilder(); - - private static final Extension BOOLEAN_BASIS_EXT = - new Extension().setUrl(POPULATION_BASIS_URL).setValue(new StringType("boolean")); - private static final Extension RESOURCE_BASIS_EXT = - new Extension().setUrl(POPULATION_BASIS_URL).setValue(new StringType("encounter")); - - @Test - void defBuilderThrowsWhenMeasureIdMissing() { - var measure = new Measure(); - var e = assertThrows(NullPointerException.class, () -> BUILDER.build(measure)); - assertTrue(e.getMessage().contains("id is required on all Resources")); - } - - @Test - void defBuilderThrowsWhenPopulationIdMissing() { - var measure = new Measure(); - measure.setId("123"); - measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode("proportion"))); - measure.addGroup().addPopulation(); - var e = assertThrows(NullPointerException.class, () -> BUILDER.build(measure)); - assertTrue(e.getMessage().contains("id is required on all Elements")); - } - - @Test - void defBuilderBooleanBasis_WithResourceExt() { - var measure = new Measure(); - measure.setId("123"); - measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode("proportion"))); - var grp = measure.addGroup().addPopulation(); - grp.setId("pop-id"); - grp.getCode().getCodingFirstRep().setCode(MeasurePopulationType.INITIALPOPULATION.toCode()); - grp.getCriteria().setExpression("test"); - measure.setExtension(Collections.singletonList(RESOURCE_BASIS_EXT)); - - var def = BUILDER.build(measure); - assertFalse(def.isBooleanBasis()); - } - - @Test - void defBuilderBooleanBasis_WithBooleanExt() { - var measure = new Measure(); - measure.setId("123"); - measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode("proportion"))); - var grp = measure.addGroup().addPopulation(); - grp.setId("pop-id"); - grp.getCode().getCodingFirstRep().setCode(MeasurePopulationType.INITIALPOPULATION.toCode()); - grp.getCriteria().setExpression("test"); - measure.setExtension(Collections.singletonList(BOOLEAN_BASIS_EXT)); - - var def = BUILDER.build(measure); - assertTrue(def.isBooleanBasis()); - } - - @Test - void defBuilderBooleanBasis_NoBasisExt() { - var measure = new Measure(); - measure.setId("123"); - measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode("proportion"))); - var grp = measure.addGroup().addPopulation(); - grp.setId("pop-id"); - grp.getCode().getCodingFirstRep().setCode(MeasurePopulationType.INITIALPOPULATION.toCode()); - grp.getCriteria().setExpression("test"); - // no extension set to define basis of Measure - var def = BUILDER.build(measure); - assertTrue(def.isBooleanBasis()); - } -} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilderTest.java deleted file mode 100644 index ea24f0b5d..000000000 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilderTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.opencds.cqf.fhir.cr.measure.r4; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.parser.IParser; -import org.hl7.fhir.r4.model.Measure; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; - -@TestInstance(Lifecycle.PER_CLASS) -class R4MeasureReportBuilderTest { - - protected R4MeasureReportBuilder measureReportBuilder; - protected FhirContext fhirContext; - - @BeforeAll - void setup() { - this.measureReportBuilder = new R4MeasureReportBuilder(); - this.fhirContext = FhirContext.forCached(FhirVersionEnum.R4); - } - - @Test - void checkIfNotBooleanBasedMeasure() { - IParser parser = fhirContext.newJsonParser(); - Measure measureEncounterBias = - (Measure) parser.parseResource(R4MeasureReportBuilderTest.class.getResourceAsStream( - "MeasureBuilderSampleWithPopulationBiasEncounter.json")); - Measure measureWithoutExtension = (Measure) parser.parseResource( - R4MeasureReportBuilderTest.class.getResourceAsStream("MeasureBuilderSampleWithoutExtension.json")); - Measure measureBooleanBias = - (Measure) parser.parseResource(R4MeasureReportBuilderTest.class.getResourceAsStream( - "MeasureBuilderSampleWithPopulationBiasBoolean.json")); - Measure measureWithEmptyExtension = (Measure) parser.parseResource( - R4MeasureReportBuilderTest.class.getResourceAsStream("MeasureBuilderSampleWithEmptyExtension.json")); - - R4MeasureBasisDef measureBasisDef = new R4MeasureBasisDef(); - - assertNotNull(measureEncounterBias); - assertFalse(measureBasisDef.isBooleanBasis(measureEncounterBias)); - - assertNotNull(measureWithoutExtension); - assertTrue(measureBasisDef.isBooleanBasis(measureWithoutExtension)); - - assertNotNull(measureBooleanBias); - assertTrue(measureBasisDef.isBooleanBasis(measureBooleanBias)); - - assertNotNull(measureWithEmptyExtension); - assertTrue(measureBasisDef.isBooleanBasis(measureWithEmptyExtension)); - } -} diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json index b195ad2d1..311bb08f0 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json @@ -75,24 +75,28 @@ "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", "group": [ { "population": [ { + "id": "initial-population", "code": { "coding": [ { "code": "initial-population" } ] } }, { + "id": "numerator", "code": { "coding": [ { "code": "numerator" } ] } }, { + "id": "denominator", "code": { "coding": [ { "code": "denominator" } ] } }, { + "id": "denominator-exclusion", "code": { "coding": [ { "code": "denominator-exclusion" diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json index 75eec48f2..71aff4779 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json @@ -75,24 +75,28 @@ "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", "group": [ { "population": [ { + "id": "initial-population", "code": { "coding": [ { "code": "initial-population" } ] } }, { + "id": "numerator", "code": { "coding": [ { "code": "numerator" } ] } }, { + "id": "denominator", "code": { "coding": [ { "code": "denominator" } ] } }, { + "id": "denominator-exclusion", "code": { "coding": [ { "code": "denominator-exclusion" diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json index 5a3f325ed..e8fab9b14 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json @@ -71,24 +71,28 @@ "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", "group": [ { "population": [ { + "id": "initial-population", "code": { "coding": [ { "code": "initial-population" } ] } }, { + "id": "numerator", "code": { "coding": [ { "code": "numerator" } ] } }, { + "id": "denominator", "code": { "coding": [ { "code": "denominator" } ] } }, { + "id": "denominator-exclusion", "code": { "coding": [ { "code": "denominator-exclusion" diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithEmptyExtension.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithEmptyExtension.json deleted file mode 100644 index 1a49cb3d7..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithEmptyExtension.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "resourceType": "Measure", - "id": "MeasureBuilderSampleWithEmptyExtension", - "meta": { - "versionId": "8", - "lastUpdated": "2022-03-01T19:49:30.831+00:00", - "source": "#cf655b4a6d955e16", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm" ] - }, - "url": "http://test-case.org/fhir/Measure/InitialInpatientPopulation", - "identifier": [ { - "system": "https://test-case.org", - "value": "initialinpatientpopulation" - } ], - "version": "0.000.01", - "name": "Initial Inpatient Population", - "title": "Glycemic Control, Initial Inpatient Population (Hypoglycemia)", - "date": "2022-03-01T11:48:58-08:00", - "description": "All inpatient encounters (including ED/Observation visits that end within 1 hour of the start of the inpatient encounter) for patients of all ages where at least one ADD was ordered or administered during the encounter that is during the performance period.", - "effectivePeriod": { - "start": "2022-01-01", - "end": "2022-12-31T23:59:59+00:00" - }, - "library": [ "http://fhir.org/guides/cqf/Library/InitialInpatientPopulation" ], - "scoring": { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "code": "cohort", - "display": "Cohort" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "code": "Outcome", - "display": "Outcome" - } ] - } ], - "rationale": "Inpatient hypoglycemia can be severe and life-threatening and is associated with longer hospital stays and increased medical costs (1-7). The prevalence of inpatient hypoglycemia varies with patient severity, hospital unit, time of episodes, and glycemic threshold. Severe hypoglycemia (<40 mg/dL) occurs in 2%-5% of hospitalized patients with diabetes mellitus (DM) while hypoglycemia <70 mg/dL has been reported in up to 10% of patients in the intensive care unit (8). Patients with DM comprise more than 25% of all U.S. inpatient stays and medication-related hypoglycemic events are common causes of adverse drug events (ADEs) occurring in inpatient settings (9,10). Rates of severe hypoglycemia vary across hospitals, suggesting opportunities for improvement in quality of care of glycemic control. Measurement of medication-related hypoglycemia in a meaningful and standardized way may improve glycemic management (11,12).", - "group": [ { - "population": [ { - "code": { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } ] - }, - "description": "Adult Inpatient Encounters", - "criteria": { - "language": "text/cql.identifier", - "expression": "Initial Population" - } - } ] - } ] -} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithPopulationBiasBoolean.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithPopulationBiasBoolean.json deleted file mode 100644 index 7ac0545ac..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithPopulationBiasBoolean.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "resourceType": "Measure", - "id": "MeasureBuilderSampleWithPopulationBiasBoolean", - "meta": { - "versionId": "8", - "lastUpdated": "2022-03-01T19:49:30.831+00:00", - "source": "#cf655b4a6d955e16", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm" ] - }, - "extension": [ { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "boolean" - } ], - "url": "http://test-case.org/fhir/Measure/InitialInpatientPopulation", - "identifier": [ { - "system": "https://test-case.org", - "value": "initialinpatientpopulation" - } ], - "version": "0.000.01", - "name": "Initial Inpatient Population", - "title": "Glycemic Control, Initial Inpatient Population (Hypoglycemia)", - "date": "2022-03-01T11:48:58-08:00", - "description": "All inpatient encounters (including ED/Observation visits that end within 1 hour of the start of the inpatient encounter) for patients of all ages where at least one ADD was ordered or administered during the encounter that is during the performance period.", - "effectivePeriod": { - "start": "2022-01-01", - "end": "2022-12-31T23:59:59+00:00" - }, - "library": [ "http://fhir.org/guides/cqf/Library/InitialInpatientPopulation" ], - "scoring": { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "code": "cohort", - "display": "Cohort" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "code": "Outcome", - "display": "Outcome" - } ] - } ], - "rationale": "Inpatient hypoglycemia can be severe and life-threatening and is associated with longer hospital stays and increased medical costs (1-7). The prevalence of inpatient hypoglycemia varies with patient severity, hospital unit, time of episodes, and glycemic threshold. Severe hypoglycemia (<40 mg/dL) occurs in 2%-5% of hospitalized patients with diabetes mellitus (DM) while hypoglycemia <70 mg/dL has been reported in up to 10% of patients in the intensive care unit (8). Patients with DM comprise more than 25% of all U.S. inpatient stays and medication-related hypoglycemic events are common causes of adverse drug events (ADEs) occurring in inpatient settings (9,10). Rates of severe hypoglycemia vary across hospitals, suggesting opportunities for improvement in quality of care of glycemic control. Measurement of medication-related hypoglycemia in a meaningful and standardized way may improve glycemic management (11,12).", - "group": [ { - "population": [ { - "code": { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } ] - }, - "description": "Adult Inpatient Encounters", - "criteria": { - "language": "text/cql.identifier", - "expression": "Initial Population" - } - } ] - } ] -} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithPopulationBiasEncounter.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithPopulationBiasEncounter.json deleted file mode 100644 index 4aa8d2354..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithPopulationBiasEncounter.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "resourceType": "Measure", - "id": "MeasureBuilderSampleWithPopulationBiasEncounter", - "meta": { - "versionId": "8", - "lastUpdated": "2022-03-01T19:49:30.831+00:00", - "source": "#cf655b4a6d955e16", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm" ] - }, - "extension": [ { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "Encounter" - } ], - "url": "http://test-case.org/fhir/Measure/InitialInpatientPopulation", - "identifier": [ { - "system": "https://test-case.org", - "value": "initialinpatientpopulation" - } ], - "version": "0.000.01", - "name": "Initial Inpatient Population", - "title": "Glycemic Control, Initial Inpatient Population (Hypoglycemia)", - "date": "2022-03-01T11:48:58-08:00", - "description": "All inpatient encounters (including ED/Observation visits that end within 1 hour of the start of the inpatient encounter) for patients of all ages where at least one ADD was ordered or administered during the encounter that is during the performance period.", - "effectivePeriod": { - "start": "2022-01-01", - "end": "2022-12-31T23:59:59+00:00" - }, - "library": [ "http://fhir.org/guides/cqf/Library/InitialInpatientPopulation" ], - "scoring": { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "code": "cohort", - "display": "Cohort" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "code": "Outcome", - "display": "Outcome" - } ] - } ], - "rationale": "Inpatient hypoglycemia can be severe and life-threatening and is associated with longer hospital stays and increased medical costs (1-7). The prevalence of inpatient hypoglycemia varies with patient severity, hospital unit, time of episodes, and glycemic threshold. Severe hypoglycemia (<40 mg/dL) occurs in 2%-5% of hospitalized patients with diabetes mellitus (DM) while hypoglycemia <70 mg/dL has been reported in up to 10% of patients in the intensive care unit (8). Patients with DM comprise more than 25% of all U.S. inpatient stays and medication-related hypoglycemic events are common causes of adverse drug events (ADEs) occurring in inpatient settings (9,10). Rates of severe hypoglycemia vary across hospitals, suggesting opportunities for improvement in quality of care of glycemic control. Measurement of medication-related hypoglycemia in a meaningful and standardized way may improve glycemic management (11,12).", - "group": [ { - "population": [ { - "code": { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } ] - }, - "description": "Adult Inpatient Encounters", - "criteria": { - "language": "text/cql.identifier", - "expression": "Initial Population" - } - } ] - } ] -} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithoutExtension.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithoutExtension.json deleted file mode 100644 index eb8b82a93..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureBuilderSampleWithoutExtension.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "resourceType": "Measure", - "id": "MeasureBuilderSample", - "meta": { - "versionId": "8", - "lastUpdated": "2022-03-01T19:49:30.831+00:00", - "source": "#cf655b4a6d955e16", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm" ] - }, - "url": "http://test-case.org/fhir/Measure/InitialInpatientPopulation", - "identifier": [ { - "system": "https://test-case.org", - "value": "initialinpatientpopulation" - } ], - "version": "0.000.01", - "name": "Initial Inpatient Population", - "title": "Glycemic Control, Initial Inpatient Population (Hypoglycemia)", - "date": "2022-03-01T11:48:58-08:00", - "description": "All inpatient encounters (including ED/Observation visits that end within 1 hour of the start of the inpatient encounter) for patients of all ages where at least one ADD was ordered or administered during the encounter that is during the performance period.", - "effectivePeriod": { - "start": "2022-01-01", - "end": "2022-12-31T23:59:59+00:00" - }, - "library": [ "http://fhir.org/guides/cqf/Library/InitialInpatientPopulation" ], - "scoring": { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "code": "cohort", - "display": "Cohort" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "code": "Outcome", - "display": "Outcome" - } ] - } ], - "rationale": "Inpatient hypoglycemia can be severe and life-threatening and is associated with longer hospital stays and increased medical costs (1-7). The prevalence of inpatient hypoglycemia varies with patient severity, hospital unit, time of episodes, and glycemic threshold. Severe hypoglycemia (<40 mg/dL) occurs in 2%-5% of hospitalized patients with diabetes mellitus (DM) while hypoglycemia <70 mg/dL has been reported in up to 10% of patients in the intensive care unit (8). Patients with DM comprise more than 25% of all U.S. inpatient stays and medication-related hypoglycemic events are common causes of adverse drug events (ADEs) occurring in inpatient settings (9,10). Rates of severe hypoglycemia vary across hospitals, suggesting opportunities for improvement in quality of care of glycemic control. Measurement of medication-related hypoglycemia in a meaningful and standardized way may improve glycemic management (11,12).", - "group": [ { - "population": [ { - "code": { - "coding": [ { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } ] - }, - "description": "Adult Inpatient Encounters", - "criteria": { - "language": "text/cql.identifier", - "expression": "Initial Population" - } - } ] - } ] -} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioGroupBooleanAllPopulations.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioGroupBooleanAllPopulations.json new file mode 100644 index 000000000..44c39b53c --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioGroupBooleanAllPopulations.json @@ -0,0 +1,108 @@ +{ + "id": "RatioGroupBooleanAllPopulations", + "resourceType": "Measure", + "url": "http://example.com/Measure/RatioGroupBooleanAllPopulations", + "library": [ + "http://example.com/Library/LibrarySimple" + ], + "scoring": { + "coding": [ + { + "system": "http://hl7.org/fhir/measure-scoring", + "code": "ratio" + } + ] + }, + "group": [ + { + "id": "group-1", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", + "valueCode": "boolean" + } + ], + "population": [ + { + "id": "initial-population", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "initial-population", + "display": "Initial Population" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Initial Population Boolean" + } + }, + { + "id": "denominator", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "denominator", + "display": "Denominator" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Denominator Boolean" + } + }, + { + "id": "denominator-exclusion", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "denominator-exclusion", + "display": "Denominator-Exclusion" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Denominator Exclusion Boolean" + } + }, + { + "id": "numerator-exclusion", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "numerator-exclusion", + "display": "Numerator Exclusion" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Numerator Exclusion Boolean" + } + }, + { + "id": "numerator", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "numerator", + "display": "Numerator" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Numerator Boolean" + } + } + ] + }] +} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioNoBasisAllPopulations.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioNoBasisAllPopulations.json new file mode 100644 index 000000000..7a6d2700f --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioNoBasisAllPopulations.json @@ -0,0 +1,102 @@ +{ + "id": "RatioNoBasisAllPopulations", + "resourceType": "Measure", + "url": "http://example.com/Measure/RatioNoBasisAllPopulations", + "library": [ + "http://example.com/Library/LibrarySimple" + ], + "scoring": { + "coding": [ + { + "system": "http://hl7.org/fhir/measure-scoring", + "code": "ratio" + } + ] + }, + "group": [ + { + "id": "group-1", + "population": [ + { + "id": "initial-population", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "initial-population", + "display": "Initial Population" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Initial Population Boolean" + } + }, + { + "id": "denominator", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "denominator", + "display": "Denominator" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Denominator Boolean" + } + }, + { + "id": "denominator-exclusion", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "denominator-exclusion", + "display": "Denominator-Exclusion" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Denominator Exclusion Boolean" + } + }, + { + "id": "numerator-exclusion", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "numerator-exclusion", + "display": "Numerator Exclusion" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Numerator Exclusion Boolean" + } + }, + { + "id": "numerator", + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "numerator", + "display": "Numerator" + } + ] + }, + "criteria": { + "language": "text/cql-identifier", + "expression": "Numerator Boolean" + } + } + ] + }] +} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/TemplateMeasure.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/TemplateMeasure.json new file mode 100644 index 000000000..24e4e5564 --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/TemplateMeasure.json @@ -0,0 +1,30 @@ +{ + "resourceType": "Measure", + "id": "TemplateMeasure", + "group": [ { + "id": "group-1", + "population": [ { + "id": "initial-population-1", + "code": { + "coding": [ { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "initial-population", + "display": "Initial Population" + } ] + } + } ] + }, + { + "id": "group-2", + "population": [ { + "id": "initial-population-2", + "code": { + "coding": [ { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "initial-population", + "display": "Initial Population" + } ] + } + } ] + }] +} \ No newline at end of file From c3c81ea938509aeb0fb4a1868757cb06749b19d7 Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:56:36 -0700 Subject: [PATCH 2/9] fix tests --- .../cr/measure/dstu3/Dstu3MeasureDefBuilder.java | 2 +- .../fhir/cr/measure/r4/R4MeasureDefBuilder.java | 5 ++++- .../cr/measure/r4/R4MeasureReportBuilder.java | 2 +- .../cr/measure/r4/MeasureDefBuilderTest.java | 16 ++++++++++++---- .../Measure-CohortResourceAllPopulations.json | 2 +- ...ContinuousVariableResourceAllPopulations.json | 2 +- ...Measure-ProportionResourceAllPopulations.json | 2 +- .../Measure-RatioResourceAllPopulations.json | 2 +- .../Measure-RatioResourceStratDifferentType.json | 2 +- .../measure/Measure-RatioResourceStratValue.json | 2 +- .../MinimalCohortResourceBasisSingleGroup.json | 2 +- ...ntinuousVariableResourceBasisSingleGroup.json | 2 +- ...inimalProportionResourceBasisSingleGroup.json | 2 +- ...nResourceBasisSingleGroupErrorPopulation.json | 2 +- ...ortionResourceBasisSingleGroupWithBadDOC.json | 2 +- ...roportionResourceBasisSingleGroupWithDOC.json | 2 +- .../MinimalRatioResourceBasisSingleGroup.json | 2 +- 17 files changed, 31 insertions(+), 20 deletions(-) diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java index c075d3bd7..442657d6b 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java @@ -62,7 +62,7 @@ public MeasureDef build(Measure measure) { // empty measure we simply generate an empty MeasureReport. // This might not be the best behavior, but we want to ensure that the behavior is the same // between versions - if (measureScoring == null) { + if (measureScoring == null && measure.hasGroup()) { throw new IllegalArgumentException("MeasureScoring must be specified on Measure"); } List groups = new ArrayList<>(); diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java index f743779b7..27d2c495d 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java @@ -287,7 +287,10 @@ public CodeDef getGroupImpNotation(MeasureGroupComponent group) { public MeasureScoring getGroupMeasureScoring(MeasureGroupComponent group) { var ext = group.getExtensionByUrl(CQFM_SCORING_EXT_URL); if (ext != null) { - return getMeasureScoring(ext.getValue().toString()); + var extVal = ext.getValue(); + assert extVal instanceof CodeableConcept; + CodeableConcept coding = (CodeableConcept) extVal; + return getMeasureScoring(coding.getCodingFirstRep().getCode()); } return null; } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilder.java index d41eca54b..35ab735ba 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureReportBuilder.java @@ -774,7 +774,7 @@ protected MeasureReport createMeasureReport( report.setMeasure(getMeasure(measure)); report.setDate(new java.util.Date()); report.setImplicitRules(measure.getImplicitRules()); - if (!measureDef.groups().get(0).isGroupImprovementNotation()) { + if (measureDef.groups().isEmpty() || !measureDef.groups().get(0).isGroupImprovementNotation()) { // if true, all group components have the same improvement Notation report.setImprovementNotation(measure.getImprovementNotation()); } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java index b32dc7d4e..f27e4c09c 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java @@ -15,7 +15,6 @@ import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; import org.opencds.cqf.fhir.cr.measure.common.MeasureDef; import org.opencds.cqf.fhir.cr.measure.common.MeasureScoring; @@ -68,7 +67,10 @@ public MeasureDef measureDefBuilder( if (group1Scoring != null) { group1.addExtension(new Extension() .setUrl(MeasureConstants.CQFM_SCORING_EXT_URL) - .setValue(new StringType(group1Scoring))); + .setValue(new CodeableConcept() + .addCoding(new Coding() + .setCode(group1Scoring) + .setSystem("http://terminology.hl7.org/CodeSystem/measure-scoring")))); } if (group1ImpNotation != null) { group1.addExtension(new Extension() @@ -87,7 +89,10 @@ public MeasureDef measureDefBuilder( if (group2Scoring != null) { group2.addExtension(new Extension() .setUrl(MeasureConstants.CQFM_SCORING_EXT_URL) - .setValue(new StringType(group2Scoring))); + .setValue(new CodeableConcept() + .addCoding(new Coding() + .setCode(group2Scoring) + .setSystem("http://terminology.hl7.org/CodeSystem/measure-scoring")))); } if (group2ImpNotation != null) { group2.addExtension(new Extension() @@ -95,7 +100,10 @@ public MeasureDef measureDefBuilder( .setValue(group2ImpNotation)); } if (measureScoring != null) { - measure.setScoring(new CodeableConcept().addCoding(new Coding().setCode(measureScoring))); + measure.setScoring(new CodeableConcept() + .addCoding(new Coding() + .setCode(measureScoring) + .setSystem("http://terminology.hl7.org/CodeSystem/measure-scoring"))); } if (measureImpNotation != null) { measure.setImprovementNotation(measureImpNotation); diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-CohortResourceAllPopulations.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-CohortResourceAllPopulations.json index 49777d1da..42a70d718 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-CohortResourceAllPopulations.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-CohortResourceAllPopulations.json @@ -8,7 +8,7 @@ "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-ContinuousVariableResourceAllPopulations.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-ContinuousVariableResourceAllPopulations.json index 25d9012ef..5b461df31 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-ContinuousVariableResourceAllPopulations.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-ContinuousVariableResourceAllPopulations.json @@ -8,7 +8,7 @@ "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-ProportionResourceAllPopulations.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-ProportionResourceAllPopulations.json index d7ee5b6be..dbfe6e8fe 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-ProportionResourceAllPopulations.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-ProportionResourceAllPopulations.json @@ -8,7 +8,7 @@ "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceAllPopulations.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceAllPopulations.json index f8fc5d23b..73290ebf1 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceAllPopulations.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceAllPopulations.json @@ -8,7 +8,7 @@ "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceStratDifferentType.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceStratDifferentType.json index b95d05f05..6dbd60208 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceStratDifferentType.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceStratDifferentType.json @@ -8,7 +8,7 @@ "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceStratValue.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceStratValue.json index f89508a87..270362ebb 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceStratValue.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MeasureTest/resources/measure/Measure-RatioResourceStratValue.json @@ -8,7 +8,7 @@ "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalCohortResourceBasisSingleGroup.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalCohortResourceBasisSingleGroup.json index 355b67854..c987e4393 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalCohortResourceBasisSingleGroup.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalCohortResourceBasisSingleGroup.json @@ -7,7 +7,7 @@ ], "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { "coding": [ diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalContinuousVariableResourceBasisSingleGroup.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalContinuousVariableResourceBasisSingleGroup.json index a0093299b..d2b3cbc8a 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalContinuousVariableResourceBasisSingleGroup.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalContinuousVariableResourceBasisSingleGroup.json @@ -7,7 +7,7 @@ ], "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { "coding": [ diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroup.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroup.json index 564887186..e409ff2ca 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroup.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroup.json @@ -7,7 +7,7 @@ ], "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" }, { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-care-gap-compatible", diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupErrorPopulation.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupErrorPopulation.json index 0903be462..44e7bfbf7 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupErrorPopulation.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupErrorPopulation.json @@ -6,7 +6,7 @@ ], "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { "coding": [ diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupWithBadDOC.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupWithBadDOC.json index 5474c4d91..250ad714a 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupWithBadDOC.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupWithBadDOC.json @@ -7,7 +7,7 @@ ], "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" }, { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-care-gap-compatible", diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupWithDOC.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupWithDOC.json index 64bae62a1..e4a311f95 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupWithDOC.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionResourceBasisSingleGroupWithDOC.json @@ -7,7 +7,7 @@ ], "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalRatioResourceBasisSingleGroup.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalRatioResourceBasisSingleGroup.json index dc445ae97..6b16c6f8f 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalRatioResourceBasisSingleGroup.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalRatioResourceBasisSingleGroup.json @@ -7,7 +7,7 @@ ], "extension": [ { "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "encounter" + "valueCode": "Encounter" } ], "scoring": { "coding": [ From 9c4ed0b7b47cb6ea2f5e67b4e2194a7e9ffe9c89 Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:07:57 -0700 Subject: [PATCH 3/9] android signature errors fix --- .../measure/dstu3/Dstu3MeasureDefBuilder.java | 17 ++++++++++------- .../cr/measure/r4/R4MeasureDefBuilder.java | 19 +++++++++++++++---- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java index 442657d6b..8a04cdcb8 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureDefBuilder.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; @@ -194,16 +193,20 @@ public CodeDef getMeasureImprovementNotation(Measure measure) { return null; } - private boolean isBooleanBasis(CodeDef populationBasis) { - return populationBasis.code().equals("boolean"); - } - private CodeDef getPopulationBasisDef(CodeDef measureBasis) { // default basis, if not defined - return Objects.requireNonNullElseGet(measureBasis, () -> new CodeDef(FHIR_ALL_TYPES_SYSTEM_URL, "boolean")); + if (measureBasis != null) { + return measureBasis; + } else { + return new CodeDef(FHIR_ALL_TYPES_SYSTEM_URL, "boolean"); + } } private CodeDef getImprovementNotation(CodeDef measureImpNotation) { - return Objects.requireNonNullElse(measureImpNotation, new CodeDef(null, IMPROVEMENT_NOTATION_SYSTEM_INCREASE)); + if (measureImpNotation != null) { + return measureImpNotation; + } else { + return new CodeDef(null, IMPROVEMENT_NOTATION_SYSTEM_INCREASE); + } } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java index 27d2c495d..84997c460 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java @@ -15,7 +15,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.CodeableConcept; @@ -308,7 +307,11 @@ private MeasureScoring getScoringDef(MeasureScoring measureScoring, MeasureScori if (groupScoring == null && measureScoring == null) { throw new IllegalArgumentException("MeasureScoring must be specified on Group or Measure"); } - return Objects.requireNonNullElse(groupScoring, measureScoring); + if (groupScoring != null) { + return groupScoring; + } else { + return measureScoring; + } } private CodeDef getPopulationBasisDef(CodeDef measureBasis, CodeDef groupBasis) { @@ -316,7 +319,7 @@ private CodeDef getPopulationBasisDef(CodeDef measureBasis, CodeDef groupBasis) // default basis, if not defined return new CodeDef(FHIR_ALL_TYPES_SYSTEM_URL, "boolean"); } - return Objects.requireNonNullElse(groupBasis, measureBasis); + return defaultCodeDef(groupBasis, measureBasis); } private CodeDef getImprovementNotation(CodeDef measureImpNotation, CodeDef groupImpNotation) { @@ -324,6 +327,14 @@ private CodeDef getImprovementNotation(CodeDef measureImpNotation, CodeDef group // default Improvement Notation, if not defined return new CodeDef(MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, IMPROVEMENT_NOTATION_SYSTEM_INCREASE); } - return Objects.requireNonNullElse(groupImpNotation, measureImpNotation); + return defaultCodeDef(groupImpNotation, measureImpNotation); + } + + private CodeDef defaultCodeDef(CodeDef code, CodeDef codeDefault) { + if (code != null) { + return code; + } else { + return codeDefault; + } } } From b904ee1c4c643f623c2325c9ece648a409026320 Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:22:56 -0700 Subject: [PATCH 4/9] sonar issue fixes --- .../cqf/fhir/cr/measure/common/GroupDef.java | 4 - .../cr/measure/common/MeasureEvaluator.java | 15 +-- .../dstu3/Dstu3MeasureReportBuilderTest.java | 52 --------- .../cr/measure/r4/MeasureDefBuilderTest.java | 19 ++-- .../cr/measure/dstu3/EXM105FHIR3Sample.json | 107 ------------------ ...05FHIR3SampleWithBoolenPopulationBias.json | 107 ------------------ .../EXM105FHIR3SampleWithoutExtension.json | 103 ----------------- 7 files changed, 13 insertions(+), 394 deletions(-) delete mode 100644 cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilderTest.java delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/GroupDef.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/GroupDef.java index 983601f9d..b8a5f09a3 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/GroupDef.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/GroupDef.java @@ -36,10 +36,6 @@ public GroupDef( this.isGroupImpNotation = isGroupImprovementNotation; this.improvementNotation = improvementNotation; this.populationBasis = populationBasis; - // this.isGroupPopulationBasis = isGroupPopulationBasis; - // this.isGroupScoring = isGroupScoring; - // this.isIncreaseImpNotation = isIncreaseImprovementNotation; - // this.isBooleanBasis = isBooleanBasis; } public String id() { diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureEvaluator.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureEvaluator.java index 5bed6ae11..32e9759a3 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureEvaluator.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/common/MeasureEvaluator.java @@ -318,7 +318,7 @@ protected void evaluateSubject( EvaluationResult evaluationResult) { evaluateSdes(subjectId, measureDef.sdes(), evaluationResult); for (GroupDef groupDef : measureDef.groups()) { - evaluateGroup(measureDef, groupDef, subjectType, subjectId, populationSize, reportType, evaluationResult); + evaluateGroup(groupDef, subjectType, subjectId, populationSize, reportType, evaluationResult); } } @@ -405,7 +405,6 @@ protected PopulationDef evaluatePopulationMembership( } protected void evaluateProportion( - MeasureDef measureDef, GroupDef groupDef, String subjectType, String subjectId, @@ -503,11 +502,7 @@ protected void evaluateProportion( } protected void evaluateContinuousVariable( - MeasureDef measureDef, - GroupDef groupDef, - String subjectType, - String subjectId, - EvaluationResult evaluationResult) { + GroupDef groupDef, String subjectType, String subjectId, EvaluationResult evaluationResult) { PopulationDef initialPopulation = groupDef.getSingle(INITIALPOPULATION); PopulationDef measurePopulation = groupDef.getSingle(MEASUREPOPULATION); PopulationDef measureObservation = groupDef.getSingle(MEASUREOBSERVATION); @@ -564,7 +559,6 @@ protected void evaluateCohort( } protected void evaluateGroup( - MeasureDef measureDef, GroupDef groupDef, String subjectType, String subjectId, @@ -577,11 +571,10 @@ protected void evaluateGroup( switch (scoring) { case PROPORTION: case RATIO: - evaluateProportion( - measureDef, groupDef, subjectType, subjectId, populationSize, reportType, evaluationResult); + evaluateProportion(groupDef, subjectType, subjectId, populationSize, reportType, evaluationResult); break; case CONTINUOUSVARIABLE: - evaluateContinuousVariable(measureDef, groupDef, subjectType, subjectId, evaluationResult); + evaluateContinuousVariable(groupDef, subjectType, subjectId, evaluationResult); break; case COHORT: evaluateCohort(groupDef, subjectType, subjectId, evaluationResult); diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilderTest.java deleted file mode 100644 index aeb405330..000000000 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilderTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.opencds.cqf.fhir.cr.measure.dstu3; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.parser.IParser; -import org.hl7.fhir.dstu3.model.Measure; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; - -@TestInstance(Lifecycle.PER_CLASS) -class Dstu3MeasureReportBuilderTest { - - protected Dstu3MeasureReportBuilder measureReportBuilder; - protected FhirContext fhirContext; - - @BeforeAll - void setup() { - this.measureReportBuilder = new Dstu3MeasureReportBuilder(); - this.fhirContext = FhirContext.forCached(FhirVersionEnum.DSTU3); - } - - @Test - void checkIfNotBooleanBasedMeasure() { - Dstu3MeasureDefBuilder defBuilder = new Dstu3MeasureDefBuilder(); - IParser parser = fhirContext.newJsonParser(); - - Measure measureEncounterBias = (Measure) - parser.parseResource(Dstu3MeasureReportBuilderTest.class.getResourceAsStream("EXM105FHIR3Sample.json")); - var measureEBDef = defBuilder.build(measureEncounterBias); - - Measure measureWithoutExtension = (Measure) parser.parseResource( - Dstu3MeasureReportBuilderTest.class.getResourceAsStream("EXM105FHIR3SampleWithoutExtension.json")); - var measureWEDef = defBuilder.build(measureWithoutExtension); - - Measure measureBooleanBias = - (Measure) parser.parseResource(Dstu3MeasureReportBuilderTest.class.getResourceAsStream( - "EXM105FHIR3SampleWithBoolenPopulationBias.json")); - var measureBBDef = defBuilder.build(measureBooleanBias); - - // Encounter Basis - /* assertNotNull(measureEncounterBias); - assertFalse(measureEBDef.isBooleanBasis()); - // No specified Basis - assertNotNull(measureWithoutExtension); - assertTrue(measureWEDef.isBooleanBasis()); - // Boolean Basis - assertNotNull(measureBooleanBias); - assertTrue(measureBBDef.isBooleanBasis());*/ - } -} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java index f27e4c09c..a57ce8925 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java @@ -21,11 +21,10 @@ import org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants; import org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants; +/** + * Test MeasureDefBuilder on different scenarios around group level Measure settings and if they are properly being set + */ public class MeasureDefBuilderTest { - // resource basis - // boolean basis - // group defined basis --> error - // no basis private final CodeableConcept increase = new CodeableConcept(new Coding( MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, IMPROVEMENT_NOTATION_SYSTEM_INCREASE, @@ -268,7 +267,7 @@ void basisPartiallyDefinedGroup() { @Test void basisInvalidGroup() { try { - var def = measureDefBuilder("fish", "cohort", decrease, "fish", "cohort", increase, null, null, null); + measureDefBuilder("fish", "cohort", decrease, "fish", "cohort", increase, null, null, null); fail("invalid code, should fail"); } catch (FHIRException e) { assertTrue(e.getMessage().contains("Unknown FHIRAllTypes code 'fish'")); @@ -278,7 +277,7 @@ void basisInvalidGroup() { @Test void BasisMeasureInvalid() { try { - var def = measureDefBuilder(null, null, null, null, null, null, "whale", "ratio", decrease); + measureDefBuilder(null, null, null, null, null, null, "whale", "ratio", decrease); fail("invalid code, should fail"); } catch (FHIRException e) { assertTrue(e.getMessage().contains("Unknown FHIRAllTypes code 'whale'")); @@ -342,7 +341,7 @@ void groupScoring() { @Test void noScoring() { try { - var def = measureDefBuilder(null, null, null, null, null, null, "boolean", null, null); + measureDefBuilder(null, null, null, null, null, null, "boolean", null, null); fail("measureScoring needs to be defined"); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains("MeasureScoring must be specified on Group or Measure")); @@ -352,7 +351,7 @@ void noScoring() { @Test void invalidMeasureScoring() { try { - var def = measureDefBuilder(null, null, null, null, null, null, "boolean", "attestation", null); + measureDefBuilder(null, null, null, null, null, null, "boolean", "attestation", null); fail("measureScoring needs to be defined"); } catch (IllegalArgumentException e) { assertTrue( @@ -363,7 +362,7 @@ void invalidMeasureScoring() { @Test void invalidGroupScoring() { try { - var def = measureDefBuilder(null, "attestation", null, null, "attestation", null, "boolean", null, null); + measureDefBuilder(null, "attestation", null, null, "attestation", null, "boolean", null, null); fail("measureScoring needs to be defined"); } catch (IllegalArgumentException e) { assertTrue( @@ -446,7 +445,7 @@ void noImprovementNotation() { @Test void invalidImprovementNotation() { try { - var def = measureDefBuilder(null, "ratio", invalid, null, "proportion", invalid, "boolean", null, null); + measureDefBuilder(null, "ratio", invalid, null, "proportion", invalid, "boolean", null, null); fail("invalid improvement Notation value"); } catch (IllegalArgumentException e) { assertTrue( diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json deleted file mode 100644 index 311bb08f0..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "resourceType": "Measure", - "id": "EXM105FHIR3Sample", - "meta": { - "versionId": "5", - "lastUpdated": "2019-12-04T17:52:12.092-07:00", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] - }, - "extension": [ { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "Encounter" - } ], - "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", - "identifier": [ { - "use": "official", - "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", - "value": "105" - } ], - "version": "8.0.000", - "name": "EXM105", - "title": "Discharged on Statin Medication", - "status": "draft", - "experimental": true, - "publisher": "The Joint Commission", - "contact": [ { - "telecom": [ { - "system": "url", - "value": "http://www.ncqa.org/" - } ] - } ], - "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "useContext": [ { - "code": { - "code": "program" - }, - "valueCodeableConcept": { - "text": "eligible-provider" - } - } ], - "jurisdiction": [ { - "coding": [ { - "system": "urn:iso:std:iso:3166", - "code": "US" - } ] - } ], - "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", - "approvalDate": "2016-01-01", - "lastReviewDate": "2019-08-19", - "effectivePeriod": { - "start": "2019-01-01", - "end": "2019-12-31" - }, - "topic": [ { - "coding": [ { - "code": "57024-2" - } ] - } ], - "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", - "scoring": { - "coding": [ { - "system": "http://hl7.org/fhir/measure-scoring", - "code": "proportion" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://hl7.org/fhir/measure-type", - "code": "process" - } ] - } ], - "rateAggregation": "none", - "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", - "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", - "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", - "group": [ { - "population": [ { - "id": "initial-population", - "code": { - "coding": [ { - "code": "initial-population" - } ] - } - }, { - "id": "numerator", - "code": { - "coding": [ { - "code": "numerator" - } ] - } - }, { - "id": "denominator", - "code": { - "coding": [ { - "code": "denominator" - } ] - } - }, { - "id": "denominator-exclusion", - "code": { - "coding": [ { - "code": "denominator-exclusion" - } ] - } - } ] - } ] -} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json deleted file mode 100644 index 71aff4779..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "resourceType": "Measure", - "id": "EXM105FHIR3Sample", - "meta": { - "versionId": "5", - "lastUpdated": "2019-12-04T17:52:12.092-07:00", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] - }, - "extension": [ { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "boolean" - } ], - "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", - "identifier": [ { - "use": "official", - "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", - "value": "105" - } ], - "version": "8.0.000", - "name": "EXM105", - "title": "Discharged on Statin Medication", - "status": "draft", - "experimental": true, - "publisher": "The Joint Commission", - "contact": [ { - "telecom": [ { - "system": "url", - "value": "http://www.ncqa.org/" - } ] - } ], - "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "useContext": [ { - "code": { - "code": "program" - }, - "valueCodeableConcept": { - "text": "eligible-provider" - } - } ], - "jurisdiction": [ { - "coding": [ { - "system": "urn:iso:std:iso:3166", - "code": "US" - } ] - } ], - "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", - "approvalDate": "2016-01-01", - "lastReviewDate": "2019-08-19", - "effectivePeriod": { - "start": "2019-01-01", - "end": "2019-12-31" - }, - "topic": [ { - "coding": [ { - "code": "57024-2" - } ] - } ], - "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", - "scoring": { - "coding": [ { - "system": "http://hl7.org/fhir/measure-scoring", - "code": "proportion" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://hl7.org/fhir/measure-type", - "code": "process" - } ] - } ], - "rateAggregation": "none", - "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", - "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", - "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", - "group": [ { - "population": [ { - "id": "initial-population", - "code": { - "coding": [ { - "code": "initial-population" - } ] - } - }, { - "id": "numerator", - "code": { - "coding": [ { - "code": "numerator" - } ] - } - }, { - "id": "denominator", - "code": { - "coding": [ { - "code": "denominator" - } ] - } - }, { - "id": "denominator-exclusion", - "code": { - "coding": [ { - "code": "denominator-exclusion" - } ] - } - } ] - } ] -} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json deleted file mode 100644 index e8fab9b14..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "resourceType": "Measure", - "id": "EXM105FHIR3Sample", - "meta": { - "versionId": "5", - "lastUpdated": "2019-12-04T17:52:12.092-07:00", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] - }, - "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", - "identifier": [ { - "use": "official", - "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", - "value": "105" - } ], - "version": "8.0.000", - "name": "EXM105", - "title": "Discharged on Statin Medication", - "status": "draft", - "experimental": true, - "publisher": "The Joint Commission", - "contact": [ { - "telecom": [ { - "system": "url", - "value": "http://www.ncqa.org/" - } ] - } ], - "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "useContext": [ { - "code": { - "code": "program" - }, - "valueCodeableConcept": { - "text": "eligible-provider" - } - } ], - "jurisdiction": [ { - "coding": [ { - "system": "urn:iso:std:iso:3166", - "code": "US" - } ] - } ], - "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", - "approvalDate": "2016-01-01", - "lastReviewDate": "2019-08-19", - "effectivePeriod": { - "start": "2019-01-01", - "end": "2019-12-31" - }, - "topic": [ { - "coding": [ { - "code": "57024-2" - } ] - } ], - "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", - "scoring": { - "coding": [ { - "system": "http://hl7.org/fhir/measure-scoring", - "code": "proportion" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://hl7.org/fhir/measure-type", - "code": "process" - } ] - } ], - "rateAggregation": "none", - "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", - "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", - "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", - "group": [ { - "population": [ { - "id": "initial-population", - "code": { - "coding": [ { - "code": "initial-population" - } ] - } - }, { - "id": "numerator", - "code": { - "coding": [ { - "code": "numerator" - } ] - } - }, { - "id": "denominator", - "code": { - "coding": [ { - "code": "denominator" - } ] - } - }, { - "id": "denominator-exclusion", - "code": { - "coding": [ { - "code": "denominator-exclusion" - } ] - } - } ] - } ] -} \ No newline at end of file From be1c993d8f1dfe516e4f29bafaffc80ea47b023f Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:59:11 -0700 Subject: [PATCH 5/9] remove unused method --- .../dstu3/Dstu3MeasureReportBuilder.java | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilder.java index ff94c35fb..efc0b8269 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/dstu3/Dstu3MeasureReportBuilder.java @@ -286,7 +286,7 @@ protected void buildPopulation( reportPopulation.setCode(measurePopulation.getCode()); reportPopulation.setId(measurePopulation.getId()); - if (measureDef.groups().get(0).isBooleanBasis()) { + if (!measureDef.groups().isEmpty() && !measureDef.groups().get(0).isBooleanBasis()) { reportPopulation.setCount(populationDef.getResources().size()); } else { reportPopulation.setCount(populationDef.getSubjects().size()); @@ -523,40 +523,6 @@ protected MeasureReport createMeasureReport( return report; } - // /** - // * Retrieve the coding from an extension that that looks like the following... - // * - // * { "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", - // * "extension": [ { "url": "ombCategory", "valueCoding": { "system": - // * "urn:oid:2.16.840.1.113883.6.238", "code": "2054-5", "display": "Black or - // * African American" } } ] } - // */ - - protected Coding getExtensionCoding(DomainResource patient, String coreCategory, String sdeCode) { - Coding valueCoding = new Coding(); - - patient.getExtension().forEach((ptExt) -> { - if (ptExt.getUrl().contains(coreCategory)) { - Coding extCoding = (Coding) ptExt.getExtension().get(0).getValue(); - String extCode = extCoding.getCode(); - if (extCode.equalsIgnoreCase(sdeCode)) { - valueCoding.setSystem(extCoding.getSystem()); - valueCoding.setCode(extCoding.getCode()); - valueCoding.setDisplay(extCoding.getDisplay()); - } - } - }); - - return valueCoding; - } - - // protected Extension createCodingExtension(String url, String codeSystem, - // String code) { - // Extension ext = new Extension().setUrl(url); - // Coding coding = new Coding().setSystem(codeSystem).setCode(code); - // ext.setValue(coding); - // return ext; - // } protected Extension createStringExtension(String url, String value) { Extension ext = new Extension().setUrl(url); From bf43fe615f75950a838f327cced563d3eba8053e Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:04:48 -0700 Subject: [PATCH 6/9] code review edits --- .../cr/measure/r4/R4MeasureDefBuilder.java | 6 +- .../cr/measure/r4/MeasureDefBuilderTest.java | 3 +- .../cr/measure/dstu3/EXM105FHIR3Sample.json | 107 ++++++++++++++++++ ...05FHIR3SampleWithBoolenPopulationBias.json | 107 ++++++++++++++++++ .../EXM105FHIR3SampleWithoutExtension.json | 103 +++++++++++++++++ 5 files changed, 321 insertions(+), 5 deletions(-) create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java index 84997c460..a821fa6e8 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java @@ -309,9 +309,8 @@ private MeasureScoring getScoringDef(MeasureScoring measureScoring, MeasureScori } if (groupScoring != null) { return groupScoring; - } else { - return measureScoring; } + return measureScoring; } private CodeDef getPopulationBasisDef(CodeDef measureBasis, CodeDef groupBasis) { @@ -333,8 +332,7 @@ private CodeDef getImprovementNotation(CodeDef measureImpNotation, CodeDef group private CodeDef defaultCodeDef(CodeDef code, CodeDef codeDefault) { if (code != null) { return code; - } else { - return codeDefault; } + return codeDefault; } } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java index a57ce8925..a380b721e 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MeasureDefBuilderTest.java @@ -15,6 +15,7 @@ import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Measure; import org.junit.jupiter.api.Test; import org.opencds.cqf.fhir.cr.measure.common.MeasureDef; import org.opencds.cqf.fhir.cr.measure.common.MeasureScoring; @@ -51,7 +52,7 @@ public MeasureDef measureDefBuilder( CodeableConcept measureImpNotation) { R4MeasureDefBuilder defBuilder = new R4MeasureDefBuilder(); - org.hl7.fhir.r4.model.Measure measure = (org.hl7.fhir.r4.model.Measure) + Measure measure = (org.hl7.fhir.r4.model.Measure) parser.parseResource(MeasureDefBuilderTest.class.getResourceAsStream("TemplateMeasure.json")); var group1 = measure.getGroup().stream() diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json new file mode 100644 index 000000000..311bb08f0 --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json @@ -0,0 +1,107 @@ +{ + "resourceType": "Measure", + "id": "EXM105FHIR3Sample", + "meta": { + "versionId": "5", + "lastUpdated": "2019-12-04T17:52:12.092-07:00", + "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] + }, + "extension": [ { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", + "valueCode": "Encounter" + } ], + "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", + "identifier": [ { + "use": "official", + "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", + "value": "105" + } ], + "version": "8.0.000", + "name": "EXM105", + "title": "Discharged on Statin Medication", + "status": "draft", + "experimental": true, + "publisher": "The Joint Commission", + "contact": [ { + "telecom": [ { + "system": "url", + "value": "http://www.ncqa.org/" + } ] + } ], + "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", + "useContext": [ { + "code": { + "code": "program" + }, + "valueCodeableConcept": { + "text": "eligible-provider" + } + } ], + "jurisdiction": [ { + "coding": [ { + "system": "urn:iso:std:iso:3166", + "code": "US" + } ] + } ], + "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", + "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", + "approvalDate": "2016-01-01", + "lastReviewDate": "2019-08-19", + "effectivePeriod": { + "start": "2019-01-01", + "end": "2019-12-31" + }, + "topic": [ { + "coding": [ { + "code": "57024-2" + } ] + } ], + "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", + "scoring": { + "coding": [ { + "system": "http://hl7.org/fhir/measure-scoring", + "code": "proportion" + } ] + }, + "type": [ { + "coding": [ { + "system": "http://hl7.org/fhir/measure-type", + "code": "process" + } ] + } ], + "rateAggregation": "none", + "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", + "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", + "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", + "group": [ { + "population": [ { + "id": "initial-population", + "code": { + "coding": [ { + "code": "initial-population" + } ] + } + }, { + "id": "numerator", + "code": { + "coding": [ { + "code": "numerator" + } ] + } + }, { + "id": "denominator", + "code": { + "coding": [ { + "code": "denominator" + } ] + } + }, { + "id": "denominator-exclusion", + "code": { + "coding": [ { + "code": "denominator-exclusion" + } ] + } + } ] + } ] +} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json new file mode 100644 index 000000000..71aff4779 --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json @@ -0,0 +1,107 @@ +{ + "resourceType": "Measure", + "id": "EXM105FHIR3Sample", + "meta": { + "versionId": "5", + "lastUpdated": "2019-12-04T17:52:12.092-07:00", + "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] + }, + "extension": [ { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", + "valueCode": "boolean" + } ], + "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", + "identifier": [ { + "use": "official", + "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", + "value": "105" + } ], + "version": "8.0.000", + "name": "EXM105", + "title": "Discharged on Statin Medication", + "status": "draft", + "experimental": true, + "publisher": "The Joint Commission", + "contact": [ { + "telecom": [ { + "system": "url", + "value": "http://www.ncqa.org/" + } ] + } ], + "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", + "useContext": [ { + "code": { + "code": "program" + }, + "valueCodeableConcept": { + "text": "eligible-provider" + } + } ], + "jurisdiction": [ { + "coding": [ { + "system": "urn:iso:std:iso:3166", + "code": "US" + } ] + } ], + "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", + "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", + "approvalDate": "2016-01-01", + "lastReviewDate": "2019-08-19", + "effectivePeriod": { + "start": "2019-01-01", + "end": "2019-12-31" + }, + "topic": [ { + "coding": [ { + "code": "57024-2" + } ] + } ], + "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", + "scoring": { + "coding": [ { + "system": "http://hl7.org/fhir/measure-scoring", + "code": "proportion" + } ] + }, + "type": [ { + "coding": [ { + "system": "http://hl7.org/fhir/measure-type", + "code": "process" + } ] + } ], + "rateAggregation": "none", + "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", + "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", + "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", + "group": [ { + "population": [ { + "id": "initial-population", + "code": { + "coding": [ { + "code": "initial-population" + } ] + } + }, { + "id": "numerator", + "code": { + "coding": [ { + "code": "numerator" + } ] + } + }, { + "id": "denominator", + "code": { + "coding": [ { + "code": "denominator" + } ] + } + }, { + "id": "denominator-exclusion", + "code": { + "coding": [ { + "code": "denominator-exclusion" + } ] + } + } ] + } ] +} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json new file mode 100644 index 000000000..e8fab9b14 --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json @@ -0,0 +1,103 @@ +{ + "resourceType": "Measure", + "id": "EXM105FHIR3Sample", + "meta": { + "versionId": "5", + "lastUpdated": "2019-12-04T17:52:12.092-07:00", + "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] + }, + "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", + "identifier": [ { + "use": "official", + "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", + "value": "105" + } ], + "version": "8.0.000", + "name": "EXM105", + "title": "Discharged on Statin Medication", + "status": "draft", + "experimental": true, + "publisher": "The Joint Commission", + "contact": [ { + "telecom": [ { + "system": "url", + "value": "http://www.ncqa.org/" + } ] + } ], + "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", + "useContext": [ { + "code": { + "code": "program" + }, + "valueCodeableConcept": { + "text": "eligible-provider" + } + } ], + "jurisdiction": [ { + "coding": [ { + "system": "urn:iso:std:iso:3166", + "code": "US" + } ] + } ], + "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", + "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", + "approvalDate": "2016-01-01", + "lastReviewDate": "2019-08-19", + "effectivePeriod": { + "start": "2019-01-01", + "end": "2019-12-31" + }, + "topic": [ { + "coding": [ { + "code": "57024-2" + } ] + } ], + "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", + "scoring": { + "coding": [ { + "system": "http://hl7.org/fhir/measure-scoring", + "code": "proportion" + } ] + }, + "type": [ { + "coding": [ { + "system": "http://hl7.org/fhir/measure-type", + "code": "process" + } ] + } ], + "rateAggregation": "none", + "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", + "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", + "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", + "group": [ { + "population": [ { + "id": "initial-population", + "code": { + "coding": [ { + "code": "initial-population" + } ] + } + }, { + "id": "numerator", + "code": { + "coding": [ { + "code": "numerator" + } ] + } + }, { + "id": "denominator", + "code": { + "coding": [ { + "code": "denominator" + } ] + } + }, { + "id": "denominator-exclusion", + "code": { + "coding": [ { + "code": "denominator-exclusion" + } ] + } + } ] + } ] +} \ No newline at end of file From e452698394d8f87f9f36fe22192e3c5e95a6f8f8 Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:05:36 -0700 Subject: [PATCH 7/9] remove unused test data --- .../cr/measure/dstu3/EXM105FHIR3Sample.json | 107 ------------------ ...05FHIR3SampleWithBoolenPopulationBias.json | 107 ------------------ .../EXM105FHIR3SampleWithoutExtension.json | 103 ----------------- 3 files changed, 317 deletions(-) delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json delete mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json deleted file mode 100644 index 311bb08f0..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3Sample.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "resourceType": "Measure", - "id": "EXM105FHIR3Sample", - "meta": { - "versionId": "5", - "lastUpdated": "2019-12-04T17:52:12.092-07:00", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] - }, - "extension": [ { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "Encounter" - } ], - "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", - "identifier": [ { - "use": "official", - "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", - "value": "105" - } ], - "version": "8.0.000", - "name": "EXM105", - "title": "Discharged on Statin Medication", - "status": "draft", - "experimental": true, - "publisher": "The Joint Commission", - "contact": [ { - "telecom": [ { - "system": "url", - "value": "http://www.ncqa.org/" - } ] - } ], - "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "useContext": [ { - "code": { - "code": "program" - }, - "valueCodeableConcept": { - "text": "eligible-provider" - } - } ], - "jurisdiction": [ { - "coding": [ { - "system": "urn:iso:std:iso:3166", - "code": "US" - } ] - } ], - "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", - "approvalDate": "2016-01-01", - "lastReviewDate": "2019-08-19", - "effectivePeriod": { - "start": "2019-01-01", - "end": "2019-12-31" - }, - "topic": [ { - "coding": [ { - "code": "57024-2" - } ] - } ], - "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", - "scoring": { - "coding": [ { - "system": "http://hl7.org/fhir/measure-scoring", - "code": "proportion" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://hl7.org/fhir/measure-type", - "code": "process" - } ] - } ], - "rateAggregation": "none", - "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", - "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", - "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", - "group": [ { - "population": [ { - "id": "initial-population", - "code": { - "coding": [ { - "code": "initial-population" - } ] - } - }, { - "id": "numerator", - "code": { - "coding": [ { - "code": "numerator" - } ] - } - }, { - "id": "denominator", - "code": { - "coding": [ { - "code": "denominator" - } ] - } - }, { - "id": "denominator-exclusion", - "code": { - "coding": [ { - "code": "denominator-exclusion" - } ] - } - } ] - } ] -} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json deleted file mode 100644 index 71aff4779..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithBoolenPopulationBias.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "resourceType": "Measure", - "id": "EXM105FHIR3Sample", - "meta": { - "versionId": "5", - "lastUpdated": "2019-12-04T17:52:12.092-07:00", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] - }, - "extension": [ { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "boolean" - } ], - "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", - "identifier": [ { - "use": "official", - "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", - "value": "105" - } ], - "version": "8.0.000", - "name": "EXM105", - "title": "Discharged on Statin Medication", - "status": "draft", - "experimental": true, - "publisher": "The Joint Commission", - "contact": [ { - "telecom": [ { - "system": "url", - "value": "http://www.ncqa.org/" - } ] - } ], - "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "useContext": [ { - "code": { - "code": "program" - }, - "valueCodeableConcept": { - "text": "eligible-provider" - } - } ], - "jurisdiction": [ { - "coding": [ { - "system": "urn:iso:std:iso:3166", - "code": "US" - } ] - } ], - "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", - "approvalDate": "2016-01-01", - "lastReviewDate": "2019-08-19", - "effectivePeriod": { - "start": "2019-01-01", - "end": "2019-12-31" - }, - "topic": [ { - "coding": [ { - "code": "57024-2" - } ] - } ], - "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", - "scoring": { - "coding": [ { - "system": "http://hl7.org/fhir/measure-scoring", - "code": "proportion" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://hl7.org/fhir/measure-type", - "code": "process" - } ] - } ], - "rateAggregation": "none", - "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", - "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", - "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", - "group": [ { - "population": [ { - "id": "initial-population", - "code": { - "coding": [ { - "code": "initial-population" - } ] - } - }, { - "id": "numerator", - "code": { - "coding": [ { - "code": "numerator" - } ] - } - }, { - "id": "denominator", - "code": { - "coding": [ { - "code": "denominator" - } ] - } - }, { - "id": "denominator-exclusion", - "code": { - "coding": [ { - "code": "denominator-exclusion" - } ] - } - } ] - } ] -} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json deleted file mode 100644 index e8fab9b14..000000000 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/dstu3/EXM105FHIR3SampleWithoutExtension.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "resourceType": "Measure", - "id": "EXM105FHIR3Sample", - "meta": { - "versionId": "5", - "lastUpdated": "2019-12-04T17:52:12.092-07:00", - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/proportion-measure-cqfm" ] - }, - "url": "http://hl7.org/fhir/us/cqfmeasures/Measure/measure-EXM105-FHIR3-8.0.000", - "identifier": [ { - "use": "official", - "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms", - "value": "105" - } ], - "version": "8.0.000", - "name": "EXM105", - "title": "Discharged on Statin Medication", - "status": "draft", - "experimental": true, - "publisher": "The Joint Commission", - "contact": [ { - "telecom": [ { - "system": "url", - "value": "http://www.ncqa.org/" - } ] - } ], - "description": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "useContext": [ { - "code": { - "code": "program" - }, - "valueCodeableConcept": { - "text": "eligible-provider" - } - } ], - "jurisdiction": [ { - "coding": [ { - "system": "urn:iso:std:iso:3166", - "code": "US" - } ] - } ], - "purpose": "Ischemic stroke patients who are prescribed or continuing to take statin medication at hospital discharge", - "copyright": "Measure specifications are in the Public Domain. LOINC(R) is a registered trademark of the Regenstrief Institute. This material contains SNOMED Clinical Terms(R) (SNOMED CT(C)) copyright 2004-2017 International Health Terminology Standards Development Organization. All rights reserved.", - "approvalDate": "2016-01-01", - "lastReviewDate": "2019-08-19", - "effectivePeriod": { - "start": "2019-01-01", - "end": "2019-12-31" - }, - "topic": [ { - "coding": [ { - "code": "57024-2" - } ] - } ], - "disclaimer": "These performance measures are not clinical guidelines and do not establish a standard of medical care, and have not been tested for all potential applications. The measures and specifications are provided without warranty.", - "scoring": { - "coding": [ { - "system": "http://hl7.org/fhir/measure-scoring", - "code": "proportion" - } ] - }, - "type": [ { - "coding": [ { - "system": "http://hl7.org/fhir/measure-type", - "code": "process" - } ] - } ], - "rateAggregation": "none", - "rationale": "There is an extensive and consistent body of evidence supporting the use of statins for secondary prevention in patients with clinically evident atherosclerotic cardiovascular disease (ASCVD), which includes individuals with ischemic stroke due to large artery atherosclerosis, individuals with ischemic stroke due to intrinsic small vessel disease, and individuals with ischemic stroke not directly due to atherosclerosis but with clinically evident atherosclerotic disease in an uninvolved cerebral or noncerebral bed. Both women and men with clinical ASCVD are at increased risk for recurrent ASCVD and ASCVD death. High-intensity statin therapy should be initiated or continued as first-line therapy in women and men less than or equal to 75 years of age who have clinical ASCVD, unless contraindicated. In patients with clinical ASCVD and a contraindication to high-intensity statin therapy, moderate-intensity therapy should be considered as an alternative if it can be tolerated. In individuals greater than 75 years of age, the potential for ASCVD risk reduction benefits, adverse effects, drug-drug interactions, and patient preferences should be considered, and statin therapy individualized based on these considerations (Stone, 2013).", - "clinicalRecommendationStatement": "For patients with stroke of atherosclerotic origin, intensive lipid lowering therapy with statins should be initiated", - "guidance": "The \"Non-elective Inpatient Encounter\" value set intends to capture all non-scheduled hospitalizations. This value set is a subset of the \"Inpatient encounter\" value set, excluding concepts that specifically refer to elective hospital admissions. Non-elective admissions include emergency, urgent and unplanned admissions.\n\nThe \"Medication, Discharge\" datatype refers to the discharge medication list and is intended to express medications ordered for post-discharge use.", - "group": [ { - "population": [ { - "id": "initial-population", - "code": { - "coding": [ { - "code": "initial-population" - } ] - } - }, { - "id": "numerator", - "code": { - "coding": [ { - "code": "numerator" - } ] - } - }, { - "id": "denominator", - "code": { - "coding": [ { - "code": "denominator" - } ] - } - }, { - "id": "denominator-exclusion", - "code": { - "coding": [ { - "code": "denominator-exclusion" - } ] - } - } ] - } ] -} \ No newline at end of file From 6f3fd6ea9d751453ba90e37ebcd9fc8d771ac9df Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:14:17 -0700 Subject: [PATCH 8/9] conditional instad of assertion --- .../fhir/cr/measure/r4/R4MeasureDefBuilder.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java index a821fa6e8..50ecaabee 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java @@ -272,13 +272,14 @@ public CodeDef getGroupImpNotation(MeasureGroupComponent group) { var ext = group.getExtensionByUrl(MEASUREREPORT_IMPROVEMENT_NOTATION_EXTENSION); if (ext != null) { var value = ext.getValue(); - assert value instanceof CodeableConcept; - CodeableConcept coding = (CodeableConcept) value; - var codeDef = new CodeDef( - coding.getCodingFirstRep().getSystem(), - coding.getCodingFirstRep().getCode()); - validateImprovementNotationCode(codeDef); - return codeDef; + if (value instanceof CodeableConcept) { + CodeableConcept coding = (CodeableConcept) value; + var codeDef = new CodeDef( + coding.getCodingFirstRep().getSystem(), + coding.getCodingFirstRep().getCode()); + validateImprovementNotationCode(codeDef); + return codeDef; + } } return null; } From 9d7f147128f0fb9db697ae4a99754328bc017702 Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:17:45 -0700 Subject: [PATCH 9/9] add annotations --- .../cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java index 50ecaabee..14ffc9095 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureDefBuilder.java @@ -12,6 +12,7 @@ import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM; import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.SDE_USAGE_CODE; +import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -208,7 +209,7 @@ private void checkId(Resource r) { } } - private MeasureScoring getMeasureScoring(String scoringCode) { + private MeasureScoring getMeasureScoring(@Nullable String scoringCode) { if (scoringCode != null) { var code = MeasureScoring.fromCode(scoringCode); if (code == null) { @@ -314,7 +315,7 @@ private MeasureScoring getScoringDef(MeasureScoring measureScoring, MeasureScori return measureScoring; } - private CodeDef getPopulationBasisDef(CodeDef measureBasis, CodeDef groupBasis) { + private CodeDef getPopulationBasisDef(@Nullable CodeDef measureBasis, @Nullable CodeDef groupBasis) { if (measureBasis == null && groupBasis == null) { // default basis, if not defined return new CodeDef(FHIR_ALL_TYPES_SYSTEM_URL, "boolean"); @@ -322,7 +323,7 @@ private CodeDef getPopulationBasisDef(CodeDef measureBasis, CodeDef groupBasis) return defaultCodeDef(groupBasis, measureBasis); } - private CodeDef getImprovementNotation(CodeDef measureImpNotation, CodeDef groupImpNotation) { + private CodeDef getImprovementNotation(@Nullable CodeDef measureImpNotation, @Nullable CodeDef groupImpNotation) { if (measureImpNotation == null && groupImpNotation == null) { // default Improvement Notation, if not defined return new CodeDef(MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, IMPROVEMENT_NOTATION_SYSTEM_INCREASE); @@ -330,7 +331,7 @@ private CodeDef getImprovementNotation(CodeDef measureImpNotation, CodeDef group return defaultCodeDef(groupImpNotation, measureImpNotation); } - private CodeDef defaultCodeDef(CodeDef code, CodeDef codeDefault) { + private CodeDef defaultCodeDef(@Nullable CodeDef code, @Nullable CodeDef codeDefault) { if (code != null) { return code; }