Skip to content

Commit

Permalink
Order population expressions for HTML output
Browse files Browse the repository at this point in the history
  • Loading branch information
elsaperelli committed Jul 6, 2023
1 parent a977000 commit 9e6c6e9
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 8 deletions.
40 changes: 33 additions & 7 deletions src/calculation/HTMLBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,49 @@ Handlebars.registerHelper('highlightCoverage', (localId, context) => {
*/
export function sortStatements(measure: fhir4.Measure, groupId: string, statements: StatementResult[]) {
const group = measure.group?.find(g => g.id === groupId) || measure.group?.[0];
const populationSet = new Set(group?.population?.map(p => p.criteria.expression));
const populationOrder = [
'initial-population',
'denominator',
'denominator-exclusion',
'denominator-exception',
'numerator',
'numerator-exclusion',
'measure-population',
'measure-population-exclusion',
'measure-observation'
];

const populationIdentifiers: Record<string, string> = {};
group?.population?.forEach(p => {

Check warning on line 108 in src/calculation/HTMLBuilder.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 108 in src/calculation/HTMLBuilder.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
if (p.code?.coding?.[0].code !== undefined) {

Check warning on line 109 in src/calculation/HTMLBuilder.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 109 in src/calculation/HTMLBuilder.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
populationIdentifiers[p.criteria.expression as string] = p.code.coding[0].code as string;
}
});

function populationCompare(a: StatementResult, b: StatementResult) {
return (
populationOrder.indexOf(populationIdentifiers[a.statementName]) -
populationOrder.indexOf(populationIdentifiers[b.statementName])
);
}

function alphaCompare(a: StatementResult, b: StatementResult) {
return a.statementName <= b.statementName ? -1 : 1;
}

statements.sort((a, b) => {
// if population statement, leave order or send to beginning
if (Object.keys(populationIdentifiers).includes(a.statementName)) {
return Object.keys(populationIdentifiers).includes(b.statementName) ? populationCompare(a, b) : -1;
}
if (Object.keys(populationIdentifiers).includes(b.statementName)) return 1;

Check warning on line 130 in src/calculation/HTMLBuilder.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 130 in src/calculation/HTMLBuilder.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

// if function, alphabetize or send to end
if (a.isFunction) {
return b.isFunction ? alphaCompare(a, b) : 1;
}
if (b.isFunction) return -1;

// if population statement, leave order or send to beginning
if (populationSet.has(a.statementName)) {
return populationSet.has(b.statementName) ? 0 : -1;
}
if (populationSet.has(b.statementName)) return 1;

// if no function or population statement, alphabetize
return alphaCompare(a, b);
});
Expand Down
89 changes: 88 additions & 1 deletion test/unit/HTMLBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
cqlLogicClauseTrueStyle,
cqlLogicClauseFalseStyle,
cqlLogicClauseCoveredStyle,
calculateClauseCoverage
calculateClauseCoverage,
sortStatements
} from '../../src/calculation/HTMLBuilder';
import {
StatementResult,
Expand All @@ -28,6 +29,7 @@ describe('HTMLBuilder', () => {
const falseStyleString = objToCSS(cqlLogicClauseFalseStyle);
const coverageStyleString = objToCSS(cqlLogicClauseCoveredStyle);
const simpleMeasure = getJSONFixture('measure/simple-measure.json') as fhir4.Measure;
const cvMeasure = getJSONFixture('measure/cv-measure.json') as fhir4.Measure;
const singlePopMeasure = <fhir4.Measure>{
resourceType: 'Measure',
status: 'unknown',
Expand Down Expand Up @@ -303,4 +305,89 @@ describe('HTMLBuilder', () => {
expect(res.indexOf('ipp')).toBeLessThan(res.indexOf('SimpleVSRetrieve'));
expect(res.indexOf('SimpleVSRetrieve')).toBeLessThan(res.indexOf('A Function'));
});

test('sortStatements orders population statements in specified order, then other, then function for a proportion boolean measure', () => {
statementResults = [
{
libraryName: 'Test',
statementName: 'A Function',
localId: 'test-id-1',
final: FinalResult.FALSE,
relevance: Relevance.TRUE,
isFunction: true
},
{
libraryName: 'Test',
statementName: 'SimpleVSRetrieve',
localId: 'test-id-2',
final: FinalResult.FALSE,
relevance: Relevance.TRUE
},
{
libraryName: 'Test',
statementName: 'Numerator',
localId: 'test-id-3',
final: FinalResult.FALSE,
relevance: Relevance.TRUE
},
{
libraryName: 'Test',
statementName: 'Initial Population',
localId: 'test-id-4',
final: FinalResult.FALSE,
relevance: Relevance.TRUE
},
{
libraryName: 'Test',
statementName: 'Denominator Exclusion',
localId: 'test-id-5',
final: FinalResult.FALSE,
relevance: Relevance.TRUE
}
];
sortStatements(simpleMeasure, 'test', statementResults);
expect(statementResults[0].statementName === 'Initial Population');
expect(statementResults[1].statementName === 'Denominator Exclusion');
expect(statementResults[2].statementName === 'Numerator');
expect(statementResults[3].statementName === 'SimpleVSRetrieve');
expect(statementResults[4].statementName === 'A Function');
});

test('sortStatements orders population statements in specified order, then other, then function for a continuous-variable boolean measure', () => {
statementResults = [
{
libraryName: 'Test',
statementName: 'Measure Population Exclusions',
localId: 'test-id-1',
final: FinalResult.FALSE,
relevance: Relevance.TRUE
},
{
libraryName: 'Test',
statementName: 'Initial Population',
localId: 'test-id-2',
final: FinalResult.FALSE,
relevance: Relevance.TRUE
},
{
libraryName: 'Test',
statementName: 'MeasureObservation',
localId: 'test-id-3',
final: FinalResult.FALSE,
relevance: Relevance.TRUE,
isFunction: true
},
{
libraryName: 'Test',
statementName: 'Measure Population',
final: FinalResult.FALSE,
relevance: Relevance.TRUE
}
];
sortStatements(cvMeasure, 'test', statementResults);
expect(statementResults[0].statementName === 'Initial Population');
expect(statementResults[1].statementName === 'Measure Population');
expect(statementResults[2].statementName === 'Measure Population Exclusions');
expect(statementResults[3].statementName === 'MeasureObservation');
});
});

0 comments on commit 9e6c6e9

Please sign in to comment.