Skip to content

Commit b7c29bb

Browse files
authored
Merge pull request #58 from mcode/empty-data-standard
Standardized empty data approach
2 parents 712653b + 87fc1cf commit b7c29bb

14 files changed

+172
-26
lines changed

src/extractors/CSVCancerRelatedMedicationExtractor.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ function formatData(medicationData) {
2323
...(medicationId && { id: medicationId }),
2424
code,
2525
codeSystem,
26-
displayText: !displayText ? null : displayText,
26+
displayText,
2727
startDate: !startDate ? null : formatDateTime(startDate),
2828
endDate: !endDate ? null : formatDateTime(endDate),
29-
treatmentReasonCode: !treatmentReasonCode ? null : treatmentReasonCode,
30-
treatmentReasonCodeSystem: !treatmentReasonCodeSystem ? null : treatmentReasonCodeSystem,
31-
treatmentReasonDisplayText: !treatmentReasonDisplayText ? null : treatmentReasonDisplayText,
32-
treatmentIntent: !treatmentIntent ? null : treatmentIntent,
29+
treatmentReasonCode,
30+
treatmentReasonCodeSystem,
31+
treatmentReasonDisplayText,
32+
treatmentIntent,
3333
status,
3434
};
3535
});

src/extractors/CSVConditionExtractor.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ function formatData(conditionData) {
2828
},
2929
category: category.split('|'),
3030
dateOfDiagnosis: !dateOfDiagnosis ? null : formatDateTime(dateOfDiagnosis),
31-
clinicalStatus: !clinicalStatus ? null : clinicalStatus,
32-
verificationStatus: !verificationStatus ? null : verificationStatus,
31+
clinicalStatus,
32+
verificationStatus,
3333
bodySite: !bodySite ? null : bodySite.split('|'),
34-
laterality: !laterality ? null : laterality,
35-
histology: !histology ? null : histology,
34+
laterality,
35+
histology,
3636
};
3737
});
3838
}

src/extractors/CSVObservationExtractor.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ function formatData(observationData) {
2222
status,
2323
code,
2424
system: codeSystem,
25-
display: !displayName ? null : displayName,
25+
display: displayName,
2626
valueCode: value,
27-
valueCodeSystem: !valueCodeSystem ? null : valueCodeSystem,
27+
valueCodeSystem,
2828
effectiveDateTime: formatDateTime(effectiveDate),
2929
bodySite,
3030
laterality,

src/extractors/CSVProcedureExtractor.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ function formatData(procedureData) {
2222
status,
2323
code,
2424
system: codeSystem,
25-
display: displayName || null,
26-
reasonCode: reasonCode || null,
27-
reasonCodeSystem: reasonCodeSystem || null,
28-
reasonDisplayName: reasonDisplayName || null,
25+
display: displayName,
26+
reasonCode,
27+
reasonCodeSystem,
28+
reasonDisplayName,
2929
conditionId,
30-
bodySite: bodySite || null,
31-
laterality: laterality || null,
30+
bodySite,
31+
laterality,
3232
effectiveDateTime: formatDateTime(effectiveDate),
33-
treatmentIntent: treatmentIntent || null,
33+
treatmentIntent,
3434
};
3535
});
3636
}

src/templates/ResourceGenerator.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ function generateResourceId(data) {
3737
return shajs('sha256').update(JSON.stringify(data)).digest('hex');
3838
}
3939

40+
// Ensures that empty data in the resource object carries a null value, rather than being undefined or an empty string
41+
function cleanEmptyData(data, depth = 0) {
42+
const cleanData = data;
43+
const MAX_DEPTH = 50;
44+
Object.keys(cleanData).forEach((key) => {
45+
if (typeof cleanData[key] === 'object' && cleanData[key]) {
46+
if (depth < MAX_DEPTH) cleanEmptyData(cleanData[key], depth + 1);
47+
else logger.warn('Maximum depth of 50 was reached while cleaning empty data on a resource, resource may not be cleaned entirely');
48+
}
49+
if (cleanData[key] === '' || cleanData[key] === undefined) {
50+
cleanData[key] = null;
51+
}
52+
});
53+
return cleanData;
54+
}
55+
4056
// Augment a data object with an ID if it doesn't have one
4157
function dataWithId(data) {
4258
return { id: generateResourceId(data), ...data };
@@ -56,10 +72,10 @@ function fillAndBundleTemplate(template, data) {
5672

5773
function generateMcodeResources(mcodeProfileID, data) {
5874
logger.debug(`Generating FHIR resource for ${mcodeProfileID} data element`);
59-
6075
const template = loadFhirTemplate(mcodeProfileID);
76+
const cleanData = _.isArray(data) ? data.map((d) => cleanEmptyData(d)) : cleanEmptyData(data);
6177
if (!template) throw new Error(`No matching profile for ${mcodeProfileID} found`);
62-
return fillAndBundleTemplate(template, data);
78+
return fillAndBundleTemplate(template, cleanData);
6379
}
6480

6581
module.exports = {

test/extractors/CSVCancerDiseaseStatusExtractor.test.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,18 @@ describe('CSVCancerDiseaseStatusExtractor', () => {
3030
// Test that valid data works fine
3131
expect(csvCancerDiseaseStatusExtractor.joinAndReformatData(exampleCSVDiseaseStatusModuleResponse)).toEqual(expect.anything());
3232

33-
// Test all required properties are
34-
delete localData[0].evidence; // Evidence is not required and will not throw an error
35-
delete localData[0].observationStatus; // Observation Status is not required and will not throw an error
33+
localData[0].evidence = ''; // Evidence is not required and will not throw an error
34+
localData[0].observationStatus = ''; // Observation Status is not required and will not throw an error
3635

3736
// Only including required properties is valid
3837
expect(csvCancerDiseaseStatusExtractor.joinAndReformatData(localData)).toEqual(expect.anything());
3938

39+
const requiredProperties = ['mrn', 'conditionId', 'diseaseStatusCode', 'dateOfObservation'];
40+
4041
// Removing each required property should throw an error
41-
Object.keys(localData[0]).forEach((key) => {
42+
requiredProperties.forEach((key) => {
4243
const clonedData = _.cloneDeep(localData);
43-
delete clonedData[0][key];
44+
clonedData[0][key] = '';
4445
expect(() => csvCancerDiseaseStatusExtractor.joinAndReformatData(clonedData)).toThrow(new Error(expectedErrorString));
4546
});
4647
});

test/templates/carePlanWithReview.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ describe('JavaScript render CarePlan template', () => {
1212
effectiveDate: '2020-01-23',
1313
hasChanged: 'false',
1414
mrn: 'abc-def',
15+
reasonCode: null,
16+
reasonDisplayText: null,
17+
name: null,
1518
};
1619

1720
const generatedCarePlan = carePlanWithReviewTemplate(CARE_PLAN_VALID_DATA);
@@ -61,6 +64,7 @@ describe('JavaScript render CarePlan template', () => {
6164
effectiveDateTime: '2020-01-23T09:07:00Z',
6265
effectiveDate: '2020-01-23',
6366
mrn: 'abc-def',
67+
haschanged: null,
6468
};
6569

6670
expect(() => carePlanWithReviewTemplate(INVALID_DATA)).toThrow(Error);

test/templates/medication.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const MEDICATION_MINIMAL_DATA = {
3636

3737

3838
const MEDICATION_INVALID_DATA = {
39-
// Omitting 'mrn', 'code', 'codesystem', fields which are required
39+
// Omitting 'mrn', 'code', 'codesystem', and 'status' fields which are required
4040
mrn: null,
4141
code: null,
4242
codeSystem: null,

test/templates/patient.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ describe('JavaScript Render Patient', () => {
1212
familyName: 'Patient',
1313
givenName: 'Test',
1414
gender: 'female',
15+
birthsex: null,
16+
dateOfBirth: null,
17+
language: null,
18+
addressLine: null,
19+
city: null,
20+
state: null,
21+
zip: null,
22+
raceCodesystem: null,
23+
raceCode: null,
24+
raceText: null,
25+
ethnicityCode: null,
26+
ethnicityText: null,
1527
};
1628
const generatedPatient = patientTemplate(PATIENT_VALID_DATA);
1729
expect(generatedPatient).toEqual(basicPatient);
@@ -77,6 +89,7 @@ describe('JavaScript Render Patient', () => {
7789
familyName: 'Patient',
7890
givenName: 'Test',
7991
gender: 'female',
92+
mrn: null,
8093
};
8194

8295
expect(() => patientTemplate(PATIENT_INVALID_DATA)).toThrow(Error);

test/templates/procedure.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ describe('JavaScript render Procedure template', () => {
1313
status: 'completed',
1414
code: '152198000',
1515
system: 'http://snomed.info/sct',
16+
display: null,
17+
reasonCode: null,
18+
reasonCodeSystem: null,
19+
reasonDisplayName: null,
20+
conditionId: null,
21+
bodySite: null,
22+
laterality: null,
23+
treatmentIntent: null,
1624
};
1725

1826
const generatedProcedure = procedureTemplate(PROCEDURE_MINIMAL_DATA);
@@ -75,6 +83,7 @@ describe('JavaScript render Procedure template', () => {
7583
effectiveDateTime: '2020-01-01',
7684
code: '152198000',
7785
system: 'http://snomed.info/sct',
86+
status: null,
7887
};
7988

8089
expect(() => procedureTemplate(INVALID_DATA)).toThrow(Error);

0 commit comments

Comments
 (0)