Skip to content

Commit 1a059b5

Browse files
committed
WIP: done with initial implementation of generic VS lookup. TODO: tests for the VS lookup and integration across repos
1 parent cca8410 commit 1a059b5

8 files changed

+90
-24
lines changed

src/helpers/conditionUtils.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
const primaryCancerConditionVS = require('../valueSets/ValueSet-onco-core-PrimaryOrUncertainBehaviorCancerDisorderVS.json');
2-
const secondaryCancerConditionVS = require('../valueSets/ValueSet-onco-core-SecondaryCancerDisorderVS.json');
1+
const { checkCodeInVS } = require('./valueSetUtils');
32

4-
function checkCodeInVS(code, valueSet) {
5-
// strips the period in the code since the provided value set has the periods removed
6-
return valueSet.compose.include[2].concept.map((c) => c.code).includes(code.replace('.', ''));
7-
}
83

94
/**
105
* Checks for ICD-10 code
@@ -28,7 +23,8 @@ function getICD10Code(condition) {
2823
* @return {boolean} if primary cancer condition
2924
*/
3025
function isConditionCodePrimary(code) {
31-
return checkCodeInVS(code, primaryCancerConditionVS);
26+
const primaryCancerConditionVSFilepath = './valueSets/ValueSet-onco-core-PrimaryOrUncertainBehaviorCancerDisorderVS.json';
27+
return checkCodeInVS(code, primaryCancerConditionVSFilepath);
3228
}
3329

3430
/**
@@ -37,7 +33,8 @@ function isConditionCodePrimary(code) {
3733
* @return {boolean} if secondary cancer condition
3834
*/
3935
function isConditionCodeSecondary(code) {
40-
return checkCodeInVS(code, secondaryCancerConditionVS);
36+
const secondaryCancerConditionVSFilepath = './valueSets/ValueSet-onco-core-SecondaryCancerDisorderVS.json';
37+
return checkCodeInVS(code, secondaryCancerConditionVSFilepath);
4138
}
4239

4340
/**

src/helpers/observationUtils.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const tumorMarkerTestVS = require('../valueSets/ValueSet-mcode-tumor-marker-test-vs.json');
1+
const { checkCodeInVS } = require('./valueSetUtils');
22

33
// Codes and display values for Vital Signs resources
44
// Code mapping is based on http://hl7.org/fhir/R4/observation-vitalsigns.html
@@ -17,12 +17,10 @@ const vitalSignsCodeToTextLookup = {
1717
'8462-4': 'Diastolic blood pressure',
1818
};
1919

20-
function checkCodeInVS(code, valueSet) {
21-
return valueSet.compose.include[0].concept.map((c) => c.code).includes(code);
22-
}
2320

2421
function isTumorMarker(code) {
25-
return checkCodeInVS(code, tumorMarkerTestVS);
22+
const tumorMarkerTestVSPath = './valueSets/ValueSet-mcode-tumor-marker-test-vs.json';
23+
return checkCodeInVS(code, tumorMarkerTestVSPath);
2624
}
2725

2826
function isVitalSign(code) {

src/helpers/valueSetUtils.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const logger = require('./logger');
4+
5+
const vsTypes = {
6+
json: 1,
7+
xml: 2,
8+
turtle: 3,
9+
};
10+
11+
function loadJsonVS(filepath) {
12+
try {
13+
const vsData = fs.readFileSync(path.resolve(__dirname, filepath));
14+
const vsJson = JSON.parse(vsData);
15+
return vsJson;
16+
} catch (error) {
17+
logger.error(`Could not load valueSet from path ${filepath}`);
18+
throw error;
19+
}
20+
}
21+
22+
function loadVS(filepath, typeOfVS = vsTypes.json) {
23+
switch (typeOfVS) {
24+
case vsTypes.json:
25+
logger.info(`loading JSON valueset from ${filepath}`);
26+
return loadJsonVS(filepath);
27+
28+
case vsTypes.xml:
29+
logger.error('No defined valueset loader for `xml` type valuesets');
30+
return null;
31+
32+
case vsTypes.turtle:
33+
logger.error('No defined valueset loader for `turtle` type valuesets');
34+
return null;
35+
36+
default:
37+
logger.error(`'${typeOfVS}' is not a recognized valueset type`);
38+
return null;
39+
}
40+
}
41+
42+
/**
43+
* Check if code is in value set
44+
* @param {object} code value to look for in a valueset
45+
* @param {object} valueSet contains list of codes included in value set
46+
* @return {boolean} true if condition is in valueSet's compose block or expansion block
47+
*/
48+
const checkCodeInVS = (code, valueSetFilePath, typeOfVS = vsTypes.json) => {
49+
const valueSet = loadVS(valueSetFilePath, typeOfVS);
50+
let inVSExpansion = false;
51+
let inVSCompose = false;
52+
if (valueSet.expansion) {
53+
// If valueSet has expansion, we only need to check these codes since everything in compose is in expansion
54+
inVSExpansion = valueSet.expansion.contains.some((containsItem) => {
55+
if (!code || !containsItem) return false;
56+
// return code.system === containsItem.system && code === containsItem.code;
57+
return code === containsItem.code;
58+
});
59+
} else {
60+
// Checks if code is in any of the valueSet.compose.include arrays
61+
inVSCompose = valueSet.compose.include.some((includeItem) => {
62+
if (!code || !includeItem || !includeItem.concept) return false;
63+
// return c.system === includeItem.system && includeItem.concept.map((concept) => concept.code).includes(code);
64+
return includeItem.concept.map((concept) => concept.code).includes(code);
65+
});
66+
}
67+
return inVSCompose || inVSExpansion;
68+
};
69+
70+
module.exports = {
71+
vsTypes,
72+
loadJsonVS,
73+
loadVS,
74+
checkCodeInVS,
75+
};

test/helpers/conditionUtils.test.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,22 @@ const examplePrimaryCondition = require('./fixtures/primary-cancer-condition.jso
1111
const exampleSecondaryCondition = require('./fixtures/secondary-cancer-condition.json');
1212
const conditionWithICD10 = require('./fixtures/condition-with-icd10.json');
1313
const conditionWithoutICD10 = require('./fixtures/condition-without-icd10.json');
14-
const primaryCancerConditionVS = require('../../src/valueSets/ValueSet-onco-core-PrimaryOrUncertainBehaviorCancerDisorderVS.json');
15-
const secondaryCancerConditionVS = require('../../src/valueSets/ValueSet-onco-core-SecondaryCancerDisorderVS.json');
1614
const icd10 = require('./fixtures/icd10.json');
1715

18-
const primaryCancerConditionCode = primaryCancerConditionVS.compose.include[2].concept[0].code;
19-
const secondaryCancerConditionCode = secondaryCancerConditionVS.compose.include[2].concept[0].code;
16+
const primaryCancerConditionCode = 'C50911'; // "Malignant neoplasm of unspecified site of right female breast"
17+
const secondaryCancerConditionCode = 'C7B1'; // "Secondary Merkel cell carcinoma" without a
2018

2119
describe('conditionUtils', () => {
2220
test('isConditionCodePrimary', () => {
2321
expect(isConditionCodePrimary(primaryCancerConditionCode)).toBeTruthy();
2422
expect(isConditionCodePrimary('anything')).toBeFalsy();
25-
expect(() => isConditionCodePrimary(undefined)).toThrowError(TypeError);
23+
expect(isConditionCodePrimary(undefined)).toBeFalsy();
2624
});
2725

2826
test('isConditionCodeSecondary', () => {
2927
expect(isConditionCodeSecondary(secondaryCancerConditionCode)).toBeTruthy();
3028
expect(isConditionCodeSecondary('anyCode')).toBeFalsy();
31-
expect(() => isConditionCodeSecondary(undefined)).toThrowError(TypeError);
29+
expect(isConditionCodeSecondary(undefined)).toBeFalsy();
3230
});
3331

3432
test('isConditionPrimary', () => {
@@ -52,7 +50,7 @@ describe('conditionUtils', () => {
5250
expect(isConditionCodeCancer(primaryCancerConditionCode)).toBeTruthy();
5351
expect(isConditionCodeCancer(secondaryCancerConditionCode)).toBeTruthy();
5452
expect(isConditionCodeCancer('anything')).toBeFalsy();
55-
expect(() => isConditionCodeCancer(undefined)).toThrowError(TypeError);
53+
expect(isConditionCodeCancer(undefined)).toBeFalsy();
5654
});
5755

5856
test('isConditionCancer', () => {

test/helpers/observationUtils.test.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const {
22
isVitalSign, isTumorMarker, isKarnofskyPerformanceStatus, isECOGPerformanceStatus, vitalSignsCodeToTextLookup,
33
} = require('../../src/helpers/observationUtils.js');
4-
const tumorMarkerTestVS = require('../../src/valueSets/ValueSet-mcode-tumor-marker-test-vs.json');
54

65
describe('observationUtils', () => {
76
test('isVitalSign should return true when passed a valid Vital Sign code', () => {
@@ -14,9 +13,8 @@ describe('observationUtils', () => {
1413
expect(isVitalSign(code)).toEqual(false);
1514
});
1615
test('isTumorMarker should return true when passed a valid Tumor marker code', () => {
17-
tumorMarkerTestVS.compose.include[0].concept.map((c) => c.code).forEach((code) => {
18-
expect(isTumorMarker(code)).toEqual(true);
19-
});
16+
const her2InTissue = '48676-1';
17+
expect(isTumorMarker(her2InTissue)).toEqual(true);
2018
});
2119
test('isTumorMarker should return false when passed a code that does not belong to a Tumor Marker', () => {
2220
const code = '12345';

0 commit comments

Comments
 (0)