Skip to content

Commit 0176990

Browse files
committed
Don't require name or gender in CSV. Mark as dataAbsentReason unknown if missing
1 parent 0fee38f commit 0176990

File tree

7 files changed

+73
-22
lines changed

7 files changed

+73
-22
lines changed

src/extractors/CSVPatientExtractor.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ function joinAndReformatData(patientData) {
1414
mrn, familyName, givenName, gender, birthsex, dateOfBirth, race, ethnicity, language, addressLine, city, state, zip,
1515
} = patientData;
1616

17-
if (!mrn || !familyName || !givenName || !gender) {
18-
throw Error('Missing required field for Patient CSV Extraction. Required values include: mrn, familyName, givenName, gender');
17+
if (!mrn) {
18+
throw Error('Missing required field for Patient CSV Extraction: mrn');
1919
}
2020

2121
return {

src/helpers/patientUtils.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ function maskPatientData(bundle, mask) {
9999
delete patient.gender;
100100
// an underscore is added when a primitive type is being replaced by an object (extension)
101101
patient._gender = masked;
102+
} else if (field === 'gender' && '_gender' in patient) {
103+
delete patient._gender; // gender may have a dataAbsentReason on it for 'unknown' data, but we'll still want to mask it
104+
patient._gender = masked;
102105
} else if (field === 'mrn' && 'identifier' in patient) {
103106
patient.identifier = [masked];
104107
} else if (field === 'name' && 'name' in patient) {
@@ -120,7 +123,7 @@ function maskPatientData(bundle, mask) {
120123
);
121124
// fhirpath.evaluate will return [] if there is no extension with the given URL
122125
// so checking if the result is [] checks if the field exists to be masked
123-
if (birthsex !== []) {
126+
if (birthsex.length > 0) {
124127
delete birthsex[0].valueCode;
125128
birthsex[0]._valueCode = masked;
126129
}
@@ -129,7 +132,7 @@ function maskPatientData(bundle, mask) {
129132
patient,
130133
'Patient.extension.where(url=\'http://hl7.org/fhir/us/core/StructureDefinition/us-core-race\')',
131134
);
132-
if (race !== []) {
135+
if (race.length > 0) {
133136
race[0].extension[0].valueCoding = masked;
134137
delete race[0].extension[1].valueString;
135138
race[0].extension[1]._valueString = masked;
@@ -139,7 +142,7 @@ function maskPatientData(bundle, mask) {
139142
patient,
140143
'Patient.extension.where(url=\'http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity\')',
141144
);
142-
if (ethnicity !== []) {
145+
if (ethnicity.length > 0) {
143146
ethnicity[0].extension[0].valueCoding = masked;
144147
delete ethnicity[0].extension[1].valueString;
145148
ethnicity[0].extension[1]._valueString = masked;

src/helpers/schemas/csv.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ const CSVConditionSchema = {
3131
const CSVPatientSchema = {
3232
headers: [
3333
{ name: 'mrn', required: true },
34-
{ name: 'familyName', required: true },
35-
{ name: 'givenName', required: true },
36-
{ name: 'gender', required: true },
34+
{ name: 'familyName' },
35+
{ name: 'givenName' },
36+
{ name: 'gender' },
3737
{ name: 'birthsex' },
3838
{ name: 'dateOfBirth' },
3939
{ name: 'race' },

src/templates/PatientTemplate.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { extensionArr, coding, valueX } = require('./snippets');
1+
const { dataAbsentReasonExtension, extensionArr, coding, valueX } = require('./snippets');
22
const { ifAllArgsObj, ifSomeArgsObj } = require('../helpers/templateUtils');
33

44
function mrnIdentifierTemplate({ mrn }) {
@@ -73,13 +73,35 @@ function birthDateTemplate({ dateOfBirth }) {
7373
};
7474
}
7575

76+
function genderTemplate({ gender }) {
77+
if (!gender) {
78+
// gender is 1..1 in mCODE
79+
return {
80+
_gender: extensionArr(dataAbsentReasonExtension('unknown')),
81+
};
82+
}
83+
84+
return {
85+
gender,
86+
};
87+
}
88+
7689
function nameTemplate({ familyName, givenName }) {
90+
if (!familyName && !givenName) {
91+
// name is 1..* in mCODE
92+
return {
93+
name: [
94+
extensionArr(dataAbsentReasonExtension('unknown')),
95+
],
96+
};
97+
}
98+
7799
return {
78100
name: [
79101
{
80102
text: `${givenName} ${familyName}`,
81-
family: familyName,
82-
given: givenName.split(' '),
103+
...(familyName && { family: familyName }),
104+
...(givenName && { given: givenName.split(' ') }),
83105
},
84106
],
85107
};
@@ -125,13 +147,13 @@ function languageTemplate({ language }) {
125147
function patientTemplate({
126148
id, mrn, familyName, givenName, gender, birthsex, dateOfBirth, language, addressLine, city, state, zip, raceCodesystem, raceCode, raceText, ethnicityCode, ethnicityText,
127149
}) {
128-
if (!(id && mrn && familyName && givenName && gender)) {
129-
throw Error('Trying to render a PatientTemplate, but a required argument is missing; ensure that id, mrn, familyName, givenName, and gender are all present');
150+
if (!(id && mrn)) {
151+
throw Error('Trying to render a PatientTemplate, but a required argument is missing; ensure that id and mrn are both present');
130152
}
131153
return {
132154
resourceType: 'Patient',
133155
id,
134-
gender,
156+
...genderTemplate({ gender }),
135157
...mrnIdentifierTemplate({ mrn }),
136158
...nameTemplate({ familyName, givenName }),
137159
...ifSomeArgsObj(addressTemplate)({ addressLine, city, state, zip }),

test/helpers/patientUtils.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,22 @@ describe('PatientUtils', () => {
9898
expect(bundle).toEqual(exampleMaskedPatient);
9999
});
100100

101+
test('should mask gender even if it only had an extension', () => {
102+
const bundle = _.cloneDeep(examplePatient);
103+
delete bundle.entry[0].resource.gender;
104+
// eslint-disable-next-line no-underscore-dangle
105+
bundle.entry[0].resource._gender = {
106+
extension: [
107+
{
108+
url: 'http://hl7.org/fhir/StructureDefinition/data-absent-reason',
109+
valueCode: 'unknown',
110+
},
111+
],
112+
};
113+
maskPatientData(bundle, ['gender', 'mrn', 'name', 'address', 'birthDate', 'language', 'ethnicity', 'birthsex', 'race']);
114+
expect(bundle).toEqual(exampleMaskedPatient);
115+
});
116+
101117
test('should throw error when provided an invalid field to mask', () => {
102118
const bundle = _.cloneDeep(examplePatient);
103119
expect(() => maskPatientData(bundle, ['this is an invalid field', 'mrn'])).toThrowError();

test/templates/fixtures/patient-resource.json

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,21 @@
1818
}
1919
],
2020
"name": [
21+
{
22+
"extension": [
23+
{
24+
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
25+
"valueCode": "unknown"
26+
}
27+
]
28+
}
29+
],
30+
"_gender": {
31+
"extension": [
2132
{
22-
"text": "Test Patient",
23-
"family": "Patient",
24-
"given": [ "Test" ]
33+
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
34+
"valueCode": "unknown"
2535
}
26-
],
27-
"gender": "female"
36+
]
37+
}
2838
}

test/templates/patient.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ describe('JavaScript Render Patient', () => {
99
const PATIENT_VALID_DATA = {
1010
id: 'SomeId',
1111
mrn: '1234',
12-
familyName: 'Patient',
13-
givenName: 'Test',
14-
gender: 'female',
12+
familyName: null,
13+
givenName: null,
14+
gender: null,
1515
birthsex: null,
1616
dateOfBirth: null,
1717
language: null,

0 commit comments

Comments
 (0)