fqm-execution
Advanced tools
Comparing version 1.3.3 to 1.4.0
@@ -37,6 +37,6 @@ "use strict"; | ||
const MeasureReportBuilder_1 = __importDefault(require("./MeasureReportBuilder")); | ||
const GapsInCareHelpers = __importStar(require("../gaps/GapsReportBuilder")); | ||
const GapsReportBuilder = __importStar(require("./GapsReportBuilder")); | ||
const HTMLBuilder_1 = require("./HTMLBuilder"); | ||
const QueryFilterParser_1 = require("../gaps/QueryFilterParser"); | ||
const RetrievesHelper = __importStar(require("../gaps/RetrievesFinder")); | ||
const QueryFilterParser_1 = require("../helpers/elm/QueryFilterParser"); | ||
const RetrievesHelper = __importStar(require("../helpers/elm/RetrievesHelper")); | ||
const CustomErrors_1 = require("../types/errors/CustomErrors"); | ||
@@ -48,3 +48,3 @@ const cql_execution_1 = require("cql-execution"); | ||
const lodash_1 = __importStar(require("lodash")); | ||
const reportBuilderFactory_1 = require("../helpers/reportBuilderFactory"); | ||
const ReportBuilderFactory_1 = require("../helpers/ReportBuilderFactory"); | ||
/** | ||
@@ -60,3 +60,3 @@ * Calculate measure against a set of patients. Returning detailed results for each patient and population group. | ||
async function calculate(measureBundle, patientBundles, options, valueSetCache = []) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; | ||
// Ensure verboseCalculationResults defaults to true when not provided in options | ||
@@ -72,4 +72,6 @@ options.verboseCalculationResults = (_a = options.verboseCalculationResults) !== null && _a !== void 0 ? _a : true; | ||
options.calculateClauseCoverage = (_d = options.calculateClauseCoverage) !== null && _d !== void 0 ? _d : true; | ||
options.disableHTMLOrdering = (_e = options.disableHTMLOrdering) !== null && _e !== void 0 ? _e : false; | ||
options.buildStatementLevelHTML = (_f = options.buildStatementLevelHTML) !== null && _f !== void 0 ? _f : false; | ||
options.calculateClauseUncoverage = (_e = options.calculateClauseUncoverage) !== null && _e !== void 0 ? _e : false; | ||
options.calculateCoverageDetails = (_f = options.calculateCoverageDetails) !== null && _f !== void 0 ? _f : false; | ||
options.disableHTMLOrdering = (_g = options.disableHTMLOrdering) !== null && _g !== void 0 ? _g : false; | ||
options.buildStatementLevelHTML = (_h = options.buildStatementLevelHTML) !== null && _h !== void 0 ? _h : false; | ||
const compositeMeasureResource = MeasureBundleHelpers.extractCompositeMeasure(measureBundle); | ||
@@ -85,2 +87,4 @@ const isCompositeExecution = compositeMeasureResource != null; | ||
let groupClauseCoverageHTML; | ||
let groupClauseUncoverageHTML; | ||
let groupClauseCoverageDetails; | ||
let newValueSetCache = [...valueSetCache]; | ||
@@ -93,7 +97,7 @@ const allELM = []; | ||
// Set the measurement period start/end, but only if the caller didn't specify one | ||
options.measurementPeriodStart = (_g = options.measurementPeriodStart) !== null && _g !== void 0 ? _g : measurementPeriod.measurementPeriodStart; | ||
options.measurementPeriodEnd = (_h = options.measurementPeriodEnd) !== null && _h !== void 0 ? _h : measurementPeriod.measurementPeriodEnd; | ||
options.measurementPeriodStart = (_j = options.measurementPeriodStart) !== null && _j !== void 0 ? _j : measurementPeriod.measurementPeriodStart; | ||
options.measurementPeriodEnd = (_k = options.measurementPeriodEnd) !== null && _k !== void 0 ? _k : measurementPeriod.measurementPeriodEnd; | ||
const results = await Execution.execute(measure, measureBundle, patientSource, options, newValueSetCache, debugObject); | ||
if (!results.rawResults) { | ||
throw new Error((_j = results.errorMessage) !== null && _j !== void 0 ? _j : 'something happened with no error message'); | ||
throw new Error((_l = results.errorMessage) !== null && _l !== void 0 ? _l : 'something happened with no error message'); | ||
} | ||
@@ -196,3 +200,4 @@ const rawResults = results.rawResults; | ||
if (!isCompositeExecution && options.calculateClauseCoverage) { | ||
groupClauseCoverageHTML = (0, HTMLBuilder_1.generateClauseCoverageHTML)(measure, executedELM, executionResults, options.disableHTMLOrdering); | ||
const coverage = (0, HTMLBuilder_1.generateClauseCoverageHTML)(measure, executedELM, executionResults, options); | ||
groupClauseCoverageHTML = coverage.coverage; | ||
overallClauseCoverageHTML = ''; | ||
@@ -214,5 +219,23 @@ Object.entries(groupClauseCoverageHTML).forEach(([groupId, result]) => { | ||
}); | ||
// pull out uncoverage html | ||
if (options.calculateClauseUncoverage && coverage.uncoverage) { | ||
groupClauseUncoverageHTML = coverage.uncoverage; | ||
if (debugObject && options.enableDebugOutput) { | ||
Object.entries(groupClauseUncoverageHTML).forEach(([groupId, result]) => { | ||
const debugUncoverageHTML = { | ||
name: `clause-uncoverage-${groupId}.html`, | ||
html: result | ||
}; | ||
if (Array.isArray(debugObject.html)) { | ||
debugObject.html.push(debugUncoverageHTML); | ||
} | ||
else { | ||
debugObject.html = [debugUncoverageHTML]; | ||
} | ||
}); | ||
} | ||
} | ||
// don't necessarily need this file, but adding it for backwards compatibility | ||
if (debugObject && options.enableDebugOutput) { | ||
(_k = debugObject.html) === null || _k === void 0 ? void 0 : _k.push({ | ||
(_m = debugObject.html) === null || _m === void 0 ? void 0 : _m.push({ | ||
name: 'overall-clause-coverage.html', | ||
@@ -222,2 +245,9 @@ html: overallClauseCoverageHTML | ||
} | ||
// grab coverage details | ||
if (options.calculateCoverageDetails && coverage.details) { | ||
groupClauseCoverageDetails = coverage.details; | ||
if (debugObject && options.enableDebugOutput) { | ||
debugObject.coverageDetails = groupClauseCoverageDetails; | ||
} | ||
} | ||
} | ||
@@ -236,3 +266,3 @@ } | ||
} | ||
return Object.assign(Object.assign(Object.assign(Object.assign({ results: prunedExecutionResults, debugOutput: debugObject }, (options.returnELM && { | ||
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ results: prunedExecutionResults, debugOutput: debugObject }, (options.returnELM && { | ||
elmLibraries: lodash_1.default.uniqWith(allELM, (libOne, libTwo) => libOne.library.identifier.id === libTwo.library.identifier.id && | ||
@@ -244,3 +274,3 @@ libOne.library.identifier.version === libOne.library.identifier.version), | ||
valueSetCache: lodash_1.default.uniqWith(newValueSetCache, (vsOne, vsTwo) => vsOne.url === vsTwo.url && vsOne.version === vsTwo.version) | ||
})), (overallClauseCoverageHTML && { coverageHTML: overallClauseCoverageHTML })), (groupClauseCoverageHTML && { groupClauseCoverageHTML: groupClauseCoverageHTML })); | ||
})), (overallClauseCoverageHTML && { coverageHTML: overallClauseCoverageHTML })), (groupClauseCoverageHTML && { groupClauseCoverageHTML: groupClauseCoverageHTML })), (groupClauseUncoverageHTML && { groupClauseUncoverageHTML: groupClauseUncoverageHTML })), (groupClauseCoverageDetails && { groupClauseCoverageDetails: groupClauseCoverageDetails })); | ||
} | ||
@@ -306,3 +336,3 @@ exports.calculate = calculate; | ||
const { results, debugOutput } = calculationResults; | ||
const builder = (0, reportBuilderFactory_1.getReportBuilder)(measureBundle, options); | ||
const builder = (0, ReportBuilderFactory_1.getReportBuilder)(measureBundle, options); | ||
builder.addAllResults(results); | ||
@@ -410,3 +440,3 @@ const report = builder.getReport(); | ||
// Add detailed info to queries based on clause results | ||
const gapsRetrieves = GapsInCareHelpers.processQueriesForGaps(baseRetrieves, dr); | ||
const gapsRetrieves = GapsReportBuilder.processQueriesForGaps(baseRetrieves, dr); | ||
const grPromises = gapsRetrieves.map(async (retrieve) => { | ||
@@ -423,8 +453,8 @@ // If the retrieves have a localId for the query and a known library name, we can get more info | ||
await Promise.all(grPromises); | ||
const { results: detailedGapsRetrieves, withErrors: reasonDetailErrors } = GapsInCareHelpers.calculateReasonDetail(gapsRetrieves, improvementNotation, dr); | ||
const { results: detailedGapsRetrieves, withErrors: reasonDetailErrors } = GapsReportBuilder.calculateReasonDetail(gapsRetrieves, improvementNotation, dr); | ||
errorLog.push(...reasonDetailErrors); | ||
const { detectedIssues, withErrors: detectedIssueErrors } = GapsInCareHelpers.generateDetectedIssueResources(detailedGapsRetrieves, matchingMeasureReport, improvementNotation); | ||
const { detectedIssues, withErrors: detectedIssueErrors } = GapsReportBuilder.generateDetectedIssueResources(detailedGapsRetrieves, matchingMeasureReport, improvementNotation); | ||
errorLog.push(...detectedIssueErrors); | ||
const patient = (_l = res.patientObject) === null || _l === void 0 ? void 0 : _l._json; | ||
const gapsBundle = GapsInCareHelpers.generateGapsInCareBundle(detectedIssues, matchingMeasureReport, patient); | ||
const gapsBundle = GapsReportBuilder.generateGapsInCareBundle(detectedIssues, matchingMeasureReport, patient); | ||
result.push(gapsBundle); | ||
@@ -431,0 +461,0 @@ if (debugOutput && options.enableDebugOutput) { |
@@ -31,3 +31,3 @@ "use strict"; | ||
exports.getSDEValues = exports.createOrSetResult = exports.setResult = exports.getResult = exports.hasResult = exports.findResult = exports.buildPopulationGroupRelevanceMap = exports.buildPopulationRelevanceMap = exports.buildPopulationRelevanceForAllEpisodes = exports.doesResultPass = exports.findResultForStatementClause = exports.setFinalResults = exports.prettyFHIRObject = exports.prettyConcept = exports.prettyCode = exports.prettyResult = exports.buildStatementAndClauseResults = exports.getStatementResult = exports.markStatementRelevant = exports.buildStatementRelevanceMap = void 0; | ||
const ClauseResultsHelpers = __importStar(require("./ClauseResultsHelpers")); | ||
const ClauseResultsHelpers = __importStar(require("../helpers/ClauseResultsHelpers")); | ||
const MeasureBundleHelpers = __importStar(require("../helpers/MeasureBundleHelpers")); | ||
@@ -34,0 +34,0 @@ const ELMDependencyHelper = __importStar(require("../helpers/elm/ELMDependencyHelpers")); |
import { ELM } from '../types/ELMTypes'; | ||
import { CalculationOptions, ClauseResult, DetailedPopulationGroupResult, ExecutionResult, StatementResult } from '../types/Calculator'; | ||
import { CalculationOptions, ClauseCoverageDetails, ClauseResult, DetailedPopulationGroupResult, ExecutionResult, StatementResult } from '../types/Calculator'; | ||
export declare const cqlLogicClauseTrueStyle: { | ||
@@ -8,2 +8,3 @@ 'background-color': string; | ||
'border-bottom-style': string; | ||
'border-bottom-width': string; | ||
}; | ||
@@ -15,2 +16,3 @@ export declare const cqlLogicClauseFalseStyle: { | ||
'border-bottom-style': string; | ||
'border-bottom-width': string; | ||
}; | ||
@@ -20,4 +22,2 @@ export declare const cqlLogicClauseCoveredStyle: { | ||
color: string; | ||
'border-bottom-color': string; | ||
'border-bottom-style': string; | ||
}; | ||
@@ -27,5 +27,7 @@ export declare const cqlLogicUncoveredClauseStyle: { | ||
color: string; | ||
'border-bottom-color': string; | ||
'border-bottom-style': string; | ||
}; | ||
export declare const cqlLogicUncoveredUncoverageClauseStyle: { | ||
'background-color': string; | ||
color: string; | ||
}; | ||
/** | ||
@@ -68,3 +70,7 @@ * Convert JS object to CSS Style string | ||
*/ | ||
export declare function generateClauseCoverageHTML(measure: fhir4.Measure, elmLibraries: ELM[], executionResults: ExecutionResult<DetailedPopulationGroupResult>[], disableHTMLOrdering?: boolean): Record<string, string>; | ||
export declare function generateClauseCoverageHTML<T extends CalculationOptions>(measure: fhir4.Measure, elmLibraries: ELM[], executionResults: ExecutionResult<DetailedPopulationGroupResult>[], options: T): { | ||
coverage: Record<string, string>; | ||
uncoverage?: Record<string, string>; | ||
details?: Record<string, ClauseCoverageDetails>; | ||
}; | ||
/** | ||
@@ -77,2 +83,6 @@ * Calculates clause coverage as the percentage of relevant clauses with FinalResult.TRUE | ||
*/ | ||
export declare function calculateClauseCoverage(relevantStatements: StatementResult[], clauseResults: ClauseResult[]): string; | ||
export declare function calculateClauseCoverage(relevantStatements: StatementResult[], clauseResults: ClauseResult[]): { | ||
percentage: string; | ||
coveredClauses: ClauseResult[]; | ||
uncoveredClauses: ClauseResult[]; | ||
}; |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.calculateClauseCoverage = exports.generateClauseCoverageHTML = exports.generateHTML = exports.sortStatements = exports.objToCSS = exports.cqlLogicUncoveredClauseStyle = exports.cqlLogicClauseCoveredStyle = exports.cqlLogicClauseFalseStyle = exports.cqlLogicClauseTrueStyle = void 0; | ||
exports.calculateClauseCoverage = exports.generateClauseCoverageHTML = exports.generateHTML = exports.sortStatements = exports.objToCSS = exports.cqlLogicUncoveredUncoverageClauseStyle = exports.cqlLogicUncoveredClauseStyle = exports.cqlLogicClauseCoveredStyle = exports.cqlLogicClauseFalseStyle = exports.cqlLogicClauseTrueStyle = void 0; | ||
const handlebars_1 = __importDefault(require("handlebars")); | ||
@@ -18,3 +18,4 @@ const Enums_1 = require("../types/Enums"); | ||
'border-bottom-color': '#20744c', | ||
'border-bottom-style': 'solid' | ||
'border-bottom-style': 'solid', | ||
'border-bottom-width': '0.35em' | ||
}; | ||
@@ -25,16 +26,17 @@ exports.cqlLogicClauseFalseStyle = { | ||
'border-bottom-color': '#a63b12', | ||
'border-bottom-style': 'double' | ||
'border-bottom-style': 'double', | ||
'border-bottom-width': '0.35em' | ||
}; | ||
exports.cqlLogicClauseCoveredStyle = { | ||
'background-color': '#daeaf5', | ||
color: '#004e82', | ||
'border-bottom-color': '#006cb4', | ||
'border-bottom-style': 'dashed' | ||
color: '#004e82' | ||
}; | ||
exports.cqlLogicUncoveredClauseStyle = { | ||
'background-color': 'white', | ||
color: 'black', | ||
'border-bottom-color': 'white', | ||
'border-bottom-style': 'solid' | ||
color: 'black' | ||
}; | ||
exports.cqlLogicUncoveredUncoverageClauseStyle = { | ||
'background-color': '#edd8d0', | ||
color: '#a63b12' | ||
}; | ||
/** | ||
@@ -86,2 +88,16 @@ * Convert JS object to CSS Style string | ||
}); | ||
// apply highlighting style to uncovered clauses | ||
handlebars_1.default.registerHelper('highlightUncoverage', (localId, context) => { | ||
const libraryName = context.data.root.libraryName; | ||
if (context.data.root.uncoveredClauses.some(result => result.libraryName === libraryName && result.localId === localId)) { | ||
// Mark with red styling if clause is found in uncoverage list | ||
return objToCSS(exports.cqlLogicUncoveredUncoverageClauseStyle); | ||
} | ||
else if (context.data.root.coveredClauses.some(result => result.libraryName === libraryName && result.localId === localId)) { | ||
// Mark with white (clear out styling) if the clause is in coverage list | ||
return objToCSS(exports.cqlLogicUncoveredClauseStyle); | ||
} | ||
// If this clause has no results then it should not be styled | ||
return ''; | ||
}); | ||
/** | ||
@@ -164,3 +180,3 @@ * Sort statements into population, then non-functions, then functions | ||
if (matchingExpression.annotation) { | ||
const statementHTML = main(Object.assign({ libraryName: s.libraryName, statementName: s.statementName, clauseResults: clauseResults }, matchingExpression.annotation[0].s)); | ||
const statementHTML = main(Object.assign(Object.assign({ libraryName: s.libraryName, statementName: s.statementName, clauseResults: clauseResults }, matchingExpression.annotation[0].s), { highlightLogic: true })); | ||
overallHTML += statementHTML; | ||
@@ -188,5 +204,7 @@ if (options === null || options === void 0 ? void 0 : options.buildStatementLevelHTML) { | ||
*/ | ||
function generateClauseCoverageHTML(measure, elmLibraries, executionResults, disableHTMLOrdering) { | ||
function generateClauseCoverageHTML(measure, elmLibraries, executionResults, options) { | ||
const groupResultLookup = {}; | ||
const htmlGroupLookup = {}; | ||
const coverageHtmlGroupLookup = {}; | ||
const uncoverageHtmlGroupLookup = {}; | ||
const coverageDetailsGroupLookup = {}; | ||
// get the detailed result for each group within each patient and add it | ||
@@ -214,3 +232,3 @@ // to the key in groupResults that matches the groupId | ||
const uniqueRelevantStatements = flattenedStatementResults.filter(s => s.relevance !== Enums_1.Relevance.NA); | ||
if (!disableHTMLOrdering) { | ||
if (!options.disableHTMLOrdering) { | ||
sortStatements(measure, groupId, uniqueRelevantStatements); | ||
@@ -237,12 +255,42 @@ } | ||
}); | ||
let htmlString = `<div><h2> ${groupId} Clause Coverage: ${calculateClauseCoverage(uniqueRelevantStatements, flattenedClauseResults)}%</h2>`; | ||
const clauseCoverage = calculateClauseCoverage(uniqueRelevantStatements, flattenedClauseResults); | ||
const uniqueCoverageClauses = clauseCoverage.coveredClauses.concat(clauseCoverage.uncoveredClauses); | ||
// setup initial html for coverage | ||
let coverageHtmlString = `<div><h2> ${groupId} Clause Coverage: ${clauseCoverage.percentage}%</h2>`; | ||
// setup initial html for uncoverage | ||
let uncoverageHtmlString = ''; | ||
if (options.calculateClauseUncoverage) { | ||
uncoverageHtmlString = `<div><h2> ${groupId} Clause Uncoverage: ${clauseCoverage.uncoveredClauses.length} of ${clauseCoverage.coveredClauses.length + clauseCoverage.uncoveredClauses.length} clauses</h2>`; | ||
} | ||
// generate HTML clauses using hbs template for each annotation | ||
statementAnnotations.forEach(a => { | ||
const res = main(Object.assign(Object.assign({ libraryName: a.libraryName, statementName: a.statementName, clauseResults: flattenedClauseResults }, a.annotation[0].s), { highlightCoverage: true })); | ||
htmlString += res; | ||
coverageHtmlString += main(Object.assign(Object.assign({ libraryName: a.libraryName, statementName: a.statementName, clauseResults: uniqueCoverageClauses }, a.annotation[0].s), { highlightCoverage: true })); | ||
// calculate for uncoverage | ||
if (options.calculateClauseUncoverage) { | ||
uncoverageHtmlString += main(Object.assign(Object.assign({ libraryName: a.libraryName, statementName: a.statementName, uncoveredClauses: clauseCoverage.uncoveredClauses, coveredClauses: clauseCoverage.coveredClauses }, a.annotation[0].s), { highlightUncoverage: true })); | ||
} | ||
}); | ||
htmlString += '</div>'; | ||
htmlGroupLookup[groupId] = htmlString; | ||
coverageHtmlString += '</div>'; | ||
uncoverageHtmlString += '</div>'; | ||
coverageHtmlGroupLookup[groupId] = coverageHtmlString; | ||
if (options.calculateClauseUncoverage) { | ||
uncoverageHtmlGroupLookup[groupId] = uncoverageHtmlString; | ||
} | ||
// If details on coverage are requested, tally them up and add them to the map. | ||
if (options.calculateCoverageDetails) { | ||
coverageDetailsGroupLookup[groupId] = { | ||
totalClauseCount: clauseCoverage.coveredClauses.length + clauseCoverage.uncoveredClauses.length, | ||
coveredClauseCount: clauseCoverage.coveredClauses.length, | ||
uncoveredClauseCount: clauseCoverage.uncoveredClauses.length, | ||
uncoveredClauses: clauseCoverage.uncoveredClauses.map(uncoveredClause => { | ||
return { | ||
localId: uncoveredClause.localId, | ||
libraryName: uncoveredClause.libraryName, | ||
statementName: uncoveredClause.statementName | ||
}; | ||
}) | ||
}; | ||
} | ||
}); | ||
return htmlGroupLookup; | ||
return Object.assign(Object.assign({ coverage: coverageHtmlGroupLookup }, (options.calculateClauseUncoverage && { uncoverage: uncoverageHtmlGroupLookup })), (options.calculateCoverageDetails && { details: coverageDetailsGroupLookup })); | ||
} | ||
@@ -263,5 +311,12 @@ exports.generateClauseCoverageHTML = generateClauseCoverageHTML; | ||
const coveredClauses = (0, lodash_1.uniqWith)(allRelevantClauses.filter(clause => clause.final === Enums_1.FinalResult.TRUE), (c1, c2) => c1.libraryName === c2.libraryName && c1.localId === c2.localId); | ||
return ((coveredClauses.length / allUniqueClauses.length) * 100).toPrecision(3); | ||
const uncoveredClauses = allUniqueClauses.filter(c => { | ||
return !coveredClauses.find(coveredC => c.libraryName === coveredC.libraryName && c.localId === coveredC.localId); | ||
}); | ||
return { | ||
percentage: ((coveredClauses.length / allUniqueClauses.length) * 100).toPrecision(3), | ||
coveredClauses, | ||
uncoveredClauses | ||
}; | ||
} | ||
exports.calculateClauseCoverage = calculateClauseCoverage; | ||
//# sourceMappingURL=HTMLBuilder.js.map |
@@ -77,2 +77,4 @@ #!/usr/bin/env node | ||
else if (commander_1.program.outputType === 'detailed') { | ||
calcOptions.calculateClauseUncoverage = true; | ||
calcOptions.calculateCoverageDetails = true; | ||
result = await (0, Calculator_1.calculate)(measureBundle, patientBundles, calcOptions, valueSetCache); | ||
@@ -192,2 +194,5 @@ } | ||
} | ||
if (debugOutput === null || debugOutput === void 0 ? void 0 : debugOutput.coverageDetails) { | ||
(0, DebugHelpers_1.dumpObject)(debugOutput.coverageDetails, 'coverageDetails.json'); | ||
} | ||
// Dump ELM | ||
@@ -194,0 +199,0 @@ if (debugOutput === null || debugOutput === void 0 ? void 0 : debugOutput.elm) { |
import { CalculationOptions, DataTypeQuery, DRCalculationOutput } from '../types/Calculator'; | ||
import { GracefulError } from '../types/errors/GracefulError'; | ||
import { EqualsFilter, InFilter, DuringFilter, AnyFilter, Filter } from '../types/QueryFilterTypes'; | ||
import { ELM, ELMIdentifier } from '../types/ELMTypes'; | ||
@@ -8,9 +7,2 @@ import { ExtractedLibrary } from '../types/CQLTypes'; | ||
/** | ||
* Take any nesting of base filters and AND filters and flatten into one list | ||
* | ||
* @param filter the root filter to flatten | ||
* @returns a list of all filters used by this query at one level | ||
*/ | ||
export declare function flattenFilters(filter: AnyFilter): AnyFilter[]; | ||
/** | ||
* Returns a FHIR library containing data requirements, given a root library | ||
@@ -20,23 +12,2 @@ */ | ||
/** | ||
* Map an EqualsFilter or InFilter into a FHIR DataRequirement codeFilter | ||
* | ||
* @param filter the filter to translate | ||
* @returns codeFilter to be put on the DataRequirement | ||
*/ | ||
export declare function generateDetailedCodeFilter(filter: EqualsFilter | InFilter, dataType?: string): fhir4.DataRequirementCodeFilter | null; | ||
/** | ||
* Map a during filter into a FHIR DataRequirement dateFilter | ||
* | ||
* @param filter the "during" filter to map | ||
* @returns dateFilter for the dateFilter list of dataRequirement | ||
*/ | ||
export declare function generateDetailedDateFilter(filter: DuringFilter): fhir4.DataRequirementDateFilter; | ||
/** | ||
* Map a filter into a FHIR DataRequirement valueFilter extension | ||
* | ||
* @param filter the filter to map | ||
* @returns extension for the valueFilter list of dataRequirement | ||
*/ | ||
export declare function generateDetailedValueFilter(filter: Filter): fhir4.Extension | GracefulError; | ||
/** | ||
* Creates query string for the data requirement using either the code filter code or valueSet and | ||
@@ -57,10 +28,2 @@ * the specified endpoint, and adds a fhirQueryPattern extension to the data requirement that | ||
/** | ||
* Given a fhir dataType as a string and an attribute as a string, returns the url which outlines | ||
* the code system used to define the valid inputs for the given attribute for the given dataType | ||
* @param dataType | ||
* @param attribute | ||
* @returns string url for code system | ||
*/ | ||
export declare function codeLookup(dataType: string, attribute: string): string | null; | ||
/** | ||
* Extracts the measurement period information from either the options or effective period (in that order depending on presence) | ||
@@ -82,1 +45,9 @@ * and populates a parameters object including the extracted info to be passed into the parseQueryInfo function | ||
export declare function getFlattenedRelatedArtifacts(measureBundle: fhir4.Bundle, rootLibRef?: string): fhir4.RelatedArtifact[]; | ||
/** | ||
* | ||
* @param q The query which contains the filters to add to the data requirement | ||
* @param dataRequirement Data requirement to add date filters to | ||
* @param withErrors Errors object which will eventually be returned to the user if populated | ||
* @returns void, but populated the dataRequirement filters | ||
*/ | ||
export declare function addFiltersToDataRequirement(q: DataTypeQuery, dataRequirement: fhir4.DataRequirement, withErrors: GracefulError[]): void; |
@@ -26,3 +26,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getFlattenedRelatedArtifacts = exports.createIntervalFromEndpoints = exports.extractDataRequirementsMeasurementPeriod = exports.codeLookup = exports.generateDataRequirement = exports.addFhirQueryPatternToDataRequirements = exports.generateDetailedValueFilter = exports.generateDetailedDateFilter = exports.generateDetailedCodeFilter = exports.getDataRequirements = exports.flattenFilters = void 0; | ||
exports.addFiltersToDataRequirement = exports.getFlattenedRelatedArtifacts = exports.createIntervalFromEndpoints = exports.extractDataRequirementsMeasurementPeriod = exports.generateDataRequirement = exports.addFhirQueryPatternToDataRequirements = exports.getDataRequirements = void 0; | ||
const PatientParameters_1 = require("../compartment-definition/PatientParameters"); | ||
@@ -32,5 +32,4 @@ const SearchParameters_1 = require("../compartment-definition/SearchParameters"); | ||
const CustomErrors_1 = require("../types/errors/CustomErrors"); | ||
const GapsInCareHelpers = __importStar(require("../gaps/GapsReportBuilder")); | ||
const QueryFilterParser_1 = require("../gaps/QueryFilterParser"); | ||
const RetrievesHelper = __importStar(require("../gaps/RetrievesFinder")); | ||
const QueryFilterParser_1 = require("./elm/QueryFilterParser"); | ||
const RetrievesHelper = __importStar(require("./elm/RetrievesHelper")); | ||
const lodash_1 = require("lodash"); | ||
@@ -42,21 +41,2 @@ const cql_execution_1 = require("cql-execution"); | ||
/** | ||
* Take any nesting of base filters and AND filters and flatten into one list | ||
* | ||
* @param filter the root filter to flatten | ||
* @returns a list of all filters used by this query at one level | ||
*/ | ||
function flattenFilters(filter) { | ||
if (filter.type !== 'and') { | ||
return [filter]; | ||
} | ||
else { | ||
const a = []; | ||
filter.children.forEach(c => { | ||
a.push(...flattenFilters(c)); | ||
}); | ||
return a; | ||
} | ||
} | ||
exports.flattenFilters = flattenFilters; | ||
/** | ||
* Returns a FHIR library containing data requirements, given a root library | ||
@@ -101,3 +81,3 @@ */ | ||
const dr = generateDataRequirement(retrieve); | ||
GapsInCareHelpers.addFiltersToDataRequirement(retrieve, dr, withErrors); | ||
addFiltersToDataRequirement(retrieve, dr, withErrors); | ||
addFhirQueryPatternToDataRequirements(dr); | ||
@@ -120,100 +100,2 @@ return dr; | ||
/** | ||
* Map an EqualsFilter or InFilter into a FHIR DataRequirement codeFilter | ||
* | ||
* @param filter the filter to translate | ||
* @returns codeFilter to be put on the DataRequirement | ||
*/ | ||
function generateDetailedCodeFilter(filter, dataType) { | ||
var _a; | ||
const system = dataType ? codeLookup(dataType, filter.attribute) : null; | ||
if (filter.type === 'equals') { | ||
const equalsFilter = filter; | ||
if (typeof equalsFilter.value === 'string') { | ||
return { | ||
path: equalsFilter.attribute, | ||
code: [ | ||
Object.assign({ code: equalsFilter.value }, (system && { system: system })) | ||
] | ||
}; | ||
} | ||
} | ||
else if (filter.type === 'in') { | ||
const inFilter = filter; | ||
if ((_a = inFilter.valueList) === null || _a === void 0 ? void 0 : _a.every(v => typeof v === 'string')) { | ||
return { | ||
path: inFilter.attribute, | ||
code: inFilter.valueList.map(v => (Object.assign({ code: v }, (system && { system: system })))) | ||
}; | ||
} | ||
else if (filter.valueCodingList) { | ||
return { | ||
path: filter.attribute, | ||
code: filter.valueCodingList | ||
}; | ||
} | ||
} | ||
return null; | ||
} | ||
exports.generateDetailedCodeFilter = generateDetailedCodeFilter; | ||
/** | ||
* Map a during filter into a FHIR DataRequirement dateFilter | ||
* | ||
* @param filter the "during" filter to map | ||
* @returns dateFilter for the dateFilter list of dataRequirement | ||
*/ | ||
function generateDetailedDateFilter(filter) { | ||
return { | ||
path: filter.attribute, | ||
valuePeriod: { start: filter.valuePeriod.start, end: filter.valuePeriod.end } | ||
}; | ||
} | ||
exports.generateDetailedDateFilter = generateDetailedDateFilter; | ||
/** | ||
* Map a filter into a FHIR DataRequirement valueFilter extension | ||
* | ||
* @param filter the filter to map | ||
* @returns extension for the valueFilter list of dataRequirement | ||
*/ | ||
function generateDetailedValueFilter(filter) { | ||
if (filter.type === 'notnull' || filter.type === 'isnull') { | ||
const nullFilter = filter; | ||
return { | ||
url: 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-valueFilter', | ||
extension: [ | ||
{ url: 'path', valueString: nullFilter.attribute }, | ||
{ url: 'comparator', valueCode: 'eq' }, | ||
{ url: 'value', valueString: nullFilter.type === 'notnull' ? 'not null' : 'null' } | ||
] | ||
}; | ||
} | ||
else if (filter.type === 'value') { | ||
const valueFilter = filter; | ||
const valueExtension = { | ||
url: 'value', | ||
valueBoolean: valueFilter.valueBoolean, | ||
valueInteger: valueFilter.valueInteger, | ||
valueString: valueFilter.valueString, | ||
valueQuantity: valueFilter.valueQuantity, | ||
valueRange: valueFilter.valueRange, | ||
valueRatio: valueFilter.valueRatio | ||
}; | ||
return { | ||
url: 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-valueFilter', | ||
extension: [ | ||
{ url: 'path', valueString: valueFilter.attribute }, | ||
{ url: 'comparator', valueCode: valueFilter.comparator }, | ||
// Remove undefineds | ||
JSON.parse(JSON.stringify(valueExtension)) | ||
] | ||
}; | ||
} | ||
else if (filter === null || filter === void 0 ? void 0 : filter.withError) { | ||
return filter.withError; | ||
} | ||
else { | ||
return { message: `Detailed value filter is not yet supported for filter type ${filter.type}` }; | ||
} | ||
} | ||
exports.generateDetailedValueFilter = generateDetailedValueFilter; | ||
/** | ||
* Creates query string for the data requirement using either the code filter code or valueSet and | ||
@@ -347,38 +229,2 @@ * the specified endpoint, and adds a fhirQueryPattern extension to the data requirement that | ||
/** | ||
* Given a fhir dataType as a string and an attribute as a string, returns the url which outlines | ||
* the code system used to define the valid inputs for the given attribute for the given dataType | ||
* @param dataType | ||
* @param attribute | ||
* @returns string url for code system | ||
*/ | ||
function codeLookup(dataType, attribute) { | ||
const validDataTypes = ['Observation', 'Procedure', 'Encounter', 'MedicationRequest']; | ||
if (!validDataTypes.includes(dataType)) { | ||
return null; | ||
} | ||
else if (dataType === 'Observation' && attribute === 'status') { | ||
return 'http://hl7.org/fhir/observation-status'; | ||
} | ||
else if (dataType === 'Procedure' && attribute === 'status') { | ||
return 'http://hl7.org/fhir/event-status'; | ||
} | ||
else if (dataType === 'Encounter' && attribute === 'status') { | ||
return 'http://hl7.org/fhir/encounter-status'; | ||
} | ||
else if (dataType === 'MedicationRequest') { | ||
switch (attribute) { | ||
case 'status': | ||
return 'http://hl7.org/fhir/CodeSystem/medicationrequest-status'; | ||
case 'intent': | ||
return 'http://hl7.org/fhir/CodeSystem/medicationrequest-intent'; | ||
case 'priority': | ||
return 'http://hl7.org/fhir/request-priority'; | ||
default: | ||
return null; | ||
} | ||
} | ||
return null; | ||
} | ||
exports.codeLookup = codeLookup; | ||
/** | ||
* Extracts the measurement period information from either the options or effective period (in that order depending on presence) | ||
@@ -491,2 +337,66 @@ * and populates a parameters object including the extracted info to be passed into the parseQueryInfo function | ||
exports.getFlattenedRelatedArtifacts = getFlattenedRelatedArtifacts; | ||
/** | ||
* | ||
* @param q The query which contains the filters to add to the data requirement | ||
* @param dataRequirement Data requirement to add date filters to | ||
* @param withErrors Errors object which will eventually be returned to the user if populated | ||
* @returns void, but populated the dataRequirement filters | ||
*/ | ||
function addFiltersToDataRequirement(q, dataRequirement, withErrors) { | ||
if (q.queryInfo) { | ||
const relevantSource = q.queryInfo.sources.find(source => source.resourceType === q.dataType); | ||
// if a source cannot be found that matches, exit the function | ||
if (relevantSource) { | ||
const detailedFilters = (0, QueryFilterParser_1.flattenFilters)(q.queryInfo.filter); | ||
detailedFilters.forEach(df => { | ||
// DuringFilter, etc. inherit from attribute filter (and have alias on them) | ||
if (relevantSource.alias === df.alias) { | ||
if (df.type === 'equals' || df.type === 'in') { | ||
const cf = (0, QueryFilterParser_1.generateDetailedCodeFilter)(df, q.dataType); | ||
if (cf !== null) { | ||
if (dataRequirement.codeFilter) { | ||
dataRequirement.codeFilter.push(cf); | ||
} | ||
else { | ||
dataRequirement.codeFilter = [cf]; | ||
} | ||
} | ||
} | ||
else if (df.type === 'during') { | ||
const dateFilter = (0, QueryFilterParser_1.generateDetailedDateFilter)(df); | ||
if (dataRequirement.dateFilter) { | ||
dataRequirement.dateFilter.push(dateFilter); | ||
} | ||
else { | ||
dataRequirement.dateFilter = [dateFilter]; | ||
} | ||
} | ||
else { | ||
const valueFilter = (0, QueryFilterParser_1.generateDetailedValueFilter)(df); | ||
if (didEncounterDetailedValueFilterErrors(valueFilter)) { | ||
withErrors.push(valueFilter); | ||
} | ||
else if (valueFilter) { | ||
if (dataRequirement.extension) { | ||
dataRequirement.extension.push(valueFilter); | ||
} | ||
else { | ||
dataRequirement.extension = [valueFilter]; | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
exports.addFiltersToDataRequirement = addFiltersToDataRequirement; | ||
function didEncounterDetailedValueFilterErrors(tbd) { | ||
if (tbd.message) { | ||
return true; | ||
} | ||
else { | ||
return false; | ||
} | ||
} | ||
//# sourceMappingURL=DataRequirementHelpers.js.map |
@@ -9,3 +9,3 @@ "use strict"; | ||
const MeasureReportBuilder_1 = __importDefault(require("../calculation/MeasureReportBuilder")); | ||
const MeasureBundleHelpers_1 = require("../helpers/MeasureBundleHelpers"); | ||
const MeasureBundleHelpers_1 = require("./MeasureBundleHelpers"); | ||
function getReportBuilder(measureBundle, options) { | ||
@@ -20,2 +20,2 @@ const compositeMeasureResource = (0, MeasureBundleHelpers_1.extractCompositeMeasure)(measureBundle); | ||
exports.getReportBuilder = getReportBuilder; | ||
//# sourceMappingURL=reportBuilderFactory.js.map | ||
//# sourceMappingURL=ReportBuilderFactory.js.map |
@@ -6,3 +6,3 @@ export * as Calculator from './calculation/Calculator'; | ||
export * as MeasureBundleHelpers from './helpers/MeasureBundleHelpers'; | ||
export * as RetrievesFinder from './gaps/RetrievesFinder'; | ||
export * as RetrievesFinder from './helpers/elm/RetrievesHelper'; | ||
export { ValueSetResolver } from './execution/ValueSetResolver'; | ||
@@ -9,0 +9,0 @@ /** |
@@ -39,3 +39,3 @@ "use strict"; | ||
exports.MeasureBundleHelpers = __importStar(require("./helpers/MeasureBundleHelpers")); | ||
exports.RetrievesFinder = __importStar(require("./gaps/RetrievesFinder")); | ||
exports.RetrievesFinder = __importStar(require("./helpers/elm/RetrievesHelper")); | ||
var ValueSetResolver_1 = require("./execution/ValueSetResolver"); | ||
@@ -42,0 +42,0 @@ Object.defineProperty(exports, "ValueSetResolver", { enumerable: true, get: function () { return ValueSetResolver_1.ValueSetResolver; } }); |
@@ -1,2 +0,2 @@ | ||
declare const _default: "{{~#if @root.highlightCoverage~}}\n<span{{#if r}} data-ref-id=\"{{r}}\" style=\"{{highlightCoverage r}}\"{{/if}}>\n{{~#if value ~}}\n{{ concat value }}\n{{~/if ~}}\n{{~#if s~}}\n{{~#each s~}}\n{{> clause ~}}\n{{~/each ~}}\n{{~/if~}}\n</span>\n{{~else~}}\n<span{{#if r}} data-ref-id=\"{{r}}\" style=\"{{highlightClause r}}\"{{/if}}>\n{{~#if value ~}}\n{{ concat value }}\n{{~/if ~}}\n{{~#if s~}}\n{{~#each s~}}\n{{> clause ~}}\n{{~/each ~}}\n{{~/if~}}\n</span>\n{{~/if~}}"; | ||
declare const _default: "{{~#if @root.highlightCoverage~}}\n<span{{#if r}} data-ref-id=\"{{r}}\" style=\"{{highlightCoverage r}}\"{{/if}}>\n{{~#if value ~}}\n{{ concat value }}\n{{~/if ~}}\n{{~#if s~}}\n{{~#each s~}}\n{{> clause ~}}\n{{~/each ~}}\n{{~/if~}}\n</span>\n{{~else~}}\n{{~#if @root.highlightUncoverage~}}\n<span{{#if r}} data-ref-id=\"{{r}}\" style=\"{{highlightUncoverage r}}\"{{/if}}>\n{{~#if value ~}}\n{{ concat value }}\n{{~/if ~}}\n{{~#if s~}}\n{{~#each s~}}\n{{> clause ~}}\n{{~/each ~}}\n{{~/if~}}\n</span>\n{{~else~}}\n<span{{#if r}} data-ref-id=\"{{r}}\" style=\"{{highlightClause r}}\"{{/if}}>\n{{~#if value ~}}\n{{ concat value }}\n{{~/if ~}}\n{{~#if s~}}\n{{~#each s~}}\n{{> clause ~}}\n{{~/each ~}}\n{{~/if~}}\n</span>\n{{~/if~}}\n{{~/if~}}"; | ||
export default _default; |
@@ -15,2 +15,14 @@ "use strict"; | ||
{{~else~}} | ||
{{~#if @root.highlightUncoverage~}} | ||
<span{{#if r}} data-ref-id="{{r}}" style="{{highlightUncoverage r}}"{{/if}}> | ||
{{~#if value ~}} | ||
{{ concat value }} | ||
{{~/if ~}} | ||
{{~#if s~}} | ||
{{~#each s~}} | ||
{{> clause ~}} | ||
{{~/each ~}} | ||
{{~/if~}} | ||
</span> | ||
{{~else~}} | ||
<span{{#if r}} data-ref-id="{{r}}" style="{{highlightClause r}}"{{/if}}> | ||
@@ -26,3 +38,4 @@ {{~#if value ~}} | ||
</span> | ||
{{~/if~}} | ||
{{~/if~}}`; | ||
//# sourceMappingURL=clause.js.map |
@@ -1,2 +0,2 @@ | ||
declare const _default: "<pre style=\"tab-size: 2; border-bottom-width: 4px; line-height: 1.4\"\n data-library-name=\"{{ libraryName }}\" data-statement-name=\"{{ statementName }}\">\n<code>\n{{> clause}}\n</code>\n</pre>"; | ||
declare const _default: "<pre style=\"tab-size: 2 {{~#if @root.highlightLogic~}}; line-height: 1.51em{{~/if~}}\"\n data-library-name=\"{{ libraryName }}\" data-statement-name=\"{{ statementName }}\">\n<code>\n{{> clause}}\n</code>\n</pre>"; | ||
export default _default; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = `<pre style="tab-size: 2; border-bottom-width: 4px; line-height: 1.4" | ||
exports.default = `<pre style="tab-size: 2 {{~#if @root.highlightLogic~}}; line-height: 1.51em{{~/if~}}" | ||
data-library-name="{{ libraryName }}" data-statement-name="{{ statementName }}"> | ||
@@ -5,0 +5,0 @@ <code> |
@@ -26,2 +26,6 @@ import { PopulationType, FinalResult, Relevance, CareGapReasonCode } from './Enums'; | ||
calculateClauseCoverage?: boolean; | ||
/** Include HTML structure with clause uncoverage highlighting. Highlights what is not covered */ | ||
calculateClauseUncoverage?: boolean; | ||
/** Include details about the clause coverage. Total clause count, total covered count, info on uncovered clauses. */ | ||
calculateCoverageDetails?: boolean; | ||
/** Enable debug output including CQL, ELM, results */ | ||
@@ -334,2 +338,3 @@ enableDebugOutput?: boolean; | ||
}; | ||
coverageDetails?: Record<string, ClauseCoverageDetails>; | ||
} | ||
@@ -361,4 +366,19 @@ export interface CalculatorFunctionOutput { | ||
groupClauseCoverageHTML?: Record<string, string>; | ||
groupClauseUncoverageHTML?: Record<string, string>; | ||
groupClauseCoverageDetails?: Record<string, ClauseCoverageDetails>; | ||
} | ||
/** | ||
* Details on clause coverage for group calculation. | ||
*/ | ||
export interface ClauseCoverageDetails { | ||
totalClauseCount: number; | ||
coveredClauseCount: number; | ||
uncoveredClauseCount: number; | ||
uncoveredClauses: { | ||
libraryName: string; | ||
statementName: string; | ||
localId: string; | ||
}[]; | ||
} | ||
/** | ||
* dataType for calculateMeasureReports() function | ||
@@ -365,0 +385,0 @@ */ |
{ | ||
"name": "fqm-execution", | ||
"version": "1.3.3", | ||
"version": "1.4.0", | ||
"description": "FHIR Quality Measure Execution", | ||
@@ -5,0 +5,0 @@ "main": "build/index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
1454222
141
20317
1327