Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/extractors/CSVPatientExtractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ function joinAndReformatData(patientData) {
mrn, familyName, givenName, gender, birthsex, dateOfBirth, race, ethnicity, language, addressLine, city, state, zip,
} = patientData;

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

return {
Expand Down
11 changes: 7 additions & 4 deletions src/helpers/patientUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ function maskPatientData(bundle, mask) {
delete patient.gender;
// an underscore is added when a primitive type is being replaced by an object (extension)
patient._gender = masked;
} else if (field === 'gender' && '_gender' in patient) {
delete patient._gender; // gender may have a dataAbsentReason on it for 'unknown' data, but we'll still want to mask it
patient._gender = masked;
} else if (field === 'mrn' && 'identifier' in patient) {
patient.identifier = [masked];
} else if (field === 'name' && 'name' in patient) {
Expand All @@ -119,8 +122,8 @@ function maskPatientData(bundle, mask) {
'Patient.extension.where(url=\'http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex\')',
);
// fhirpath.evaluate will return [] if there is no extension with the given URL
// so checking if the result is [] checks if the field exists to be masked
if (birthsex !== []) {
// so checking if the result is an array with anything in it checks if the field exists to be masked
if (birthsex.length > 0) {
delete birthsex[0].valueCode;
birthsex[0]._valueCode = masked;
}
Expand All @@ -129,7 +132,7 @@ function maskPatientData(bundle, mask) {
patient,
'Patient.extension.where(url=\'http://hl7.org/fhir/us/core/StructureDefinition/us-core-race\')',
);
if (race !== []) {
if (race.length > 0) {
race[0].extension[0].valueCoding = masked;
delete race[0].extension[1].valueString;
race[0].extension[1]._valueString = masked;
Expand All @@ -139,7 +142,7 @@ function maskPatientData(bundle, mask) {
patient,
'Patient.extension.where(url=\'http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity\')',
);
if (ethnicity !== []) {
if (ethnicity.length > 0) {
ethnicity[0].extension[0].valueCoding = masked;
delete ethnicity[0].extension[1].valueString;
ethnicity[0].extension[1]._valueString = masked;
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/schemas/csv.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const CSVConditionSchema = {
const CSVPatientSchema = {
headers: [
{ name: 'mrn', required: true },
{ name: 'familyName', required: true },
{ name: 'givenName', required: true },
{ name: 'gender', required: true },
{ name: 'familyName' },
{ name: 'givenName' },
{ name: 'gender' },
{ name: 'birthsex' },
{ name: 'dateOfBirth' },
{ name: 'race' },
Expand Down
34 changes: 28 additions & 6 deletions src/templates/PatientTemplate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { extensionArr, coding, valueX } = require('./snippets');
const { dataAbsentReasonExtension, extensionArr, coding, valueX } = require('./snippets');
const { ifAllArgsObj, ifSomeArgsObj } = require('../helpers/templateUtils');

function mrnIdentifierTemplate({ mrn }) {
Expand Down Expand Up @@ -73,13 +73,35 @@ function birthDateTemplate({ dateOfBirth }) {
};
}

function genderTemplate({ gender }) {
if (!gender) {
// gender is 1..1 in mCODE
return {
_gender: extensionArr(dataAbsentReasonExtension('unknown')),
};
}

return {
gender,
};
}

function nameTemplate({ familyName, givenName }) {
if (!familyName && !givenName) {
// name is 1..* in mCODE
return {
name: [
extensionArr(dataAbsentReasonExtension('unknown')),
],
};
}

return {
name: [
{
text: `${givenName} ${familyName}`,
family: familyName,
given: givenName.split(' '),
...(familyName && { family: familyName }),
...(givenName && { given: givenName.split(' ') }),
},
],
};
Expand Down Expand Up @@ -125,13 +147,13 @@ function languageTemplate({ language }) {
function patientTemplate({
id, mrn, familyName, givenName, gender, birthsex, dateOfBirth, language, addressLine, city, state, zip, raceCodesystem, raceCode, raceText, ethnicityCode, ethnicityText,
}) {
if (!(id && mrn && familyName && givenName && gender)) {
throw Error('Trying to render a PatientTemplate, but a required argument is missing; ensure that id, mrn, familyName, givenName, and gender are all present');
if (!(id && mrn)) {
throw Error('Trying to render a PatientTemplate, but a required argument is missing; ensure that id and mrn are both present');
}
return {
resourceType: 'Patient',
id,
gender,
...genderTemplate({ gender }),
...mrnIdentifierTemplate({ mrn }),
...nameTemplate({ familyName, givenName }),
...ifSomeArgsObj(addressTemplate)({ addressLine, city, state, zip }),
Expand Down
16 changes: 16 additions & 0 deletions test/helpers/patientUtils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ describe('PatientUtils', () => {
expect(bundle).toEqual(exampleMaskedPatient);
});

test('should mask gender even if it only had an extension', () => {
const bundle = _.cloneDeep(examplePatient);
delete bundle.entry[0].resource.gender;
// eslint-disable-next-line no-underscore-dangle
bundle.entry[0].resource._gender = {
extension: [
{
url: 'http://hl7.org/fhir/StructureDefinition/data-absent-reason',
valueCode: 'unknown',
},
],
};
maskPatientData(bundle, ['gender', 'mrn', 'name', 'address', 'birthDate', 'language', 'ethnicity', 'birthsex', 'race']);
expect(bundle).toEqual(exampleMaskedPatient);
});

test('should throw error when provided an invalid field to mask', () => {
const bundle = _.cloneDeep(examplePatient);
expect(() => maskPatientData(bundle, ['this is an invalid field', 'mrn'])).toThrowError();
Expand Down
20 changes: 15 additions & 5 deletions test/templates/fixtures/patient-resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,21 @@
}
],
"name": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
"valueCode": "unknown"
}
]
}
],
"_gender": {
"extension": [
{
"text": "Test Patient",
"family": "Patient",
"given": [ "Test" ]
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
"valueCode": "unknown"
}
],
"gender": "female"
]
}
}
6 changes: 3 additions & 3 deletions test/templates/patient.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ describe('JavaScript Render Patient', () => {
const PATIENT_VALID_DATA = {
id: 'SomeId',
mrn: '1234',
familyName: 'Patient',
givenName: 'Test',
gender: 'female',
familyName: null,
givenName: null,
gender: null,
birthsex: null,
dateOfBirth: null,
language: null,
Expand Down