Surface empty stratifiers as contained OperationOutcomes instead of failing the Measure#1021
Draft
lukedegruchy wants to merge 1 commit into
Draft
Surface empty stratifiers as contained OperationOutcomes instead of failing the Measure#1021lukedegruchy wants to merge 1 commit into
lukedegruchy wants to merge 1 commit into
Conversation
…ailing the Measure Follow-up to #1019. Previously, R4MeasureDefBuilder.getStratifierType threw InvalidMeasureDefinitionException when a stratifier had no criteria.expression and no components, aborting the entire Measure evaluation. Now the rest of the Measure evaluates and the offending stratifiers are reported as contained OperationOutcomes on the MeasureReport. Build-time changes (R4MeasureDefBuilder): - Detect empty stratifiers in buildStratifierDef and emit a placeholder StratifierDef with a null type and no components. The placeholder is a no-op for the evaluator (empty components list) and the report builder (no strata produced), and keeps the 1:1 ordering with Measure.group.stratifier that R4MeasureReportBuilder.buildGroup asserts on. - Collect a non-fatal error per offending stratifier in a buildErrors list on the builder, then push them onto MeasureDef.errors after the MeasureDef is constructed so the existing OperationOutcome pipeline picks them up. - Drop the now-unused InvalidMeasureDefinitionException class. Report-builder change (R4MeasureReportBuilderContext): - addContained deduplicates by fhirType()/idPart, and freshly-built OperationOutcomes all collapsed to "OperationOutcome/", so only the first error ever made it into the report. addOperationOutcomes now assigns each OperationOutcome a distinct id (operation-outcome-N) so every MeasureDef error surfaces as its own contained resource. Test changes: - The regression test now reuses LibrarySimple.cql/LibrarySimple.json (no enhancements needed — all referenced defines already exist) and the standalone EmptyStratifier.cql and EmptyStratifier Library JSON are removed. - MeasureStratifierTest.emptyStratifier asserts a contained OperationOutcome for both stratifier-1 and stratifier-2, that the group still has both stratifier slots, and that Initial Population evaluates over the expected 10 patients. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Formatting check succeeded! |
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Follow-up to #1019. The empty-stratifier validation introduced in that PR
threw
InvalidMeasureDefinitionExceptionfromR4MeasureDefBuilder,which aborted the entire
$evaluate-measurecall before any populationswere calculated. For real-world MADIE exports that contain one
malformed stratifier alongside otherwise-valid populations, this meant
the consumer got nothing usable back. This branch downgrades the
empty-stratifier case to a non-fatal report-level error: the rest of
the measure evaluates as normal and each offending stratifier produces
its own contained
OperationOutcomeon the returnedMeasureReport.R4MeasureDefBuilderno longer throws when a stratifier has nocriteria.expressionand no components. It builds a placeholderStratifierDef(null type, no components) and records the error,which is later attached to the
MeasureDefso the existingcontained-OperationOutcome pipeline picks it up. The placeholder
evaluates to a no-op on both the evaluator and the report builder
while preserving the 1:1 ordering with
Measure.group.stratifier.R4MeasureReportBuilderContext.addOperationOutcomesnow assignseach generated
OperationOutcomea distinct id (operation-outcome-N)so that multiple errors are no longer collapsed by
addContained'sputIfAbsentdedup-by-id.InvalidMeasureDefinitionExceptionclass is removed.LibrarySimplelibrary/CQL — no separate
EmptyStratifier.cqlor library JSON,no enhancements to
LibrarySimple.cqlwere needed since allreferenced defines already exist.