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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"axios": "^0.18.0",
"base64url": "^3.0.0",
"classnames": "^2.2.6",
"clone": "^2.1.2",
"fhirclient": "^2.3.1",
"json-server": "^0.16.3",
"jsrsasign": "^10.2.0",
Expand Down
25 changes: 20 additions & 5 deletions src/components/RequestBox/RequestBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Component } from "react";
import FHIR from "fhirclient";
import SMARTBox from "../SMARTBox/SMARTBox";
import PatientBox from "../SMARTBox/PatientBox";
import CheckBox from '../Inputs/CheckBox';
import { defaultValues, shortNameMap } from "../../util/data";
import { getAge } from "../../util/fhir";
import _ from "lodash";
Expand All @@ -23,7 +24,8 @@ export default class RequestBox extends Component {
display: null,
request: {},
gatherCount: 0,
response: {}
response: {},
deidentifyRecords: false
};

this.renderRequestResources = this.renderRequestResources.bind(this);
Expand All @@ -33,6 +35,7 @@ export default class RequestBox extends Component {
this.renderPrefetchedResources = this.renderPrefetchedResources.bind(this);
this.renderError = this.renderError.bind(this);
this.buildLaunchLink = this.buildLaunchLink.bind(this);
this.updateDeidentifyCheckbox = this.updateDeidentifyCheckbox.bind(this);
}

// TODO - see how to submit response for alternative therapy
Expand All @@ -42,7 +45,7 @@ export default class RequestBox extends Component {
// Prepare the prefetch.
const prefetch = this.prepPrefetch();
// Submit the CRD request.
this.props.submitInfo(prefetch, request, this.state.patient, "order-sign");
this.props.submitInfo(prefetch, request, this.state.patient, "order-sign", this.state.deidentifyRecords);
}

componentDidMount() {}
Expand All @@ -68,7 +71,8 @@ export default class RequestBox extends Component {
this.prepPrefetch(),
this.state.request,
this.state.patient,
"order-sign"
"order-sign",
this.state.deidentifyRecords
);
}
};
Expand Down Expand Up @@ -326,6 +330,10 @@ export default class RequestBox extends Component {
return Object.keys(this.state.request).length === 0;
}

updateDeidentifyCheckbox(elementName, value) {
this.setState({ deidentifyRecords: value });
}

render() {
const params = {};
params['serverUrl'] = this.props.ehrUrl;
Expand Down Expand Up @@ -379,8 +387,15 @@ export default class RequestBox extends Component {
<div>
{this.renderPatientInfo()}
{this.renderPrefetchedResources()}
</div>

</div>
<div>
<b>Deidentify Records</b>
<CheckBox
toggle = {this.state.deidentifyRecords}
updateCB={this.updateDeidentifyCheckbox}
elementName = "deidentifyCheckbox"
/>
</div>
</div>
</div>
<button className={"submit-btn btn btn-class "} onClick={this.relaunch} disabled={disableLaunchDTR}>
Expand Down
4 changes: 2 additions & 2 deletions src/containers/RequestBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,14 @@ export default class RequestBuilder extends Component {

}

submit_info(prefetch, request, patient, hook) {
submit_info(prefetch, request, patient, hook, deidentifyRecords) {
this.consoleLog("Initiating form submission", types.info);
this.setState({patient});
const hookConfig = {
"includeConfig": this.state.includeConfig,
"alternativeTherapy": this.state.alternativeTherapy
}
let json_request = buildRequest(request, patient, this.state.ehrUrl, this.state.token, prefetch, this.state.sendPrefetch, hook, hookConfig);
let json_request = buildRequest(request, patient, this.state.ehrUrl, this.state.token, prefetch, this.state.sendPrefetch, hook, hookConfig, deidentifyRecords);
let cdsUrl = this.state.cdsUrl;
if (hook === "order-sign") {
cdsUrl = cdsUrl + "/" + this.state.orderSign;
Expand Down
34 changes: 33 additions & 1 deletion src/util/buildRequest.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@

export default function buildRequest(request, patient, ehrUrl, token, prefetch, includePrefetch, hook, hookConfig) {
import deidentifyPatient from "./deidentifyPatient";
import deidentifyCoverage from "./deidentifyCoverage";
import clone from 'clone'


export default function buildRequest(request, patient, ehrUrl, token, prefetch, includePrefetch, hook, hookConfig, deidentifyRecords) {
if (deidentifyRecords) {
// make a copy of the resources before modifying
let newPrefetch = clone(prefetch);

console.log("Deidentify Patient and Coverage Resources to remove PHI");
// loop through the prefetch looking for the patient and the coverage
newPrefetch.forEach((bundle) => {
bundle.forEach((resource) => {
let resourceType = resource.resource.resourceType;
if (resourceType == "Patient") {
// deidentify the patient
let patient = deidentifyPatient(resource.resource);
// replace the patient resource with the deidentified version
resource.resource = patient
} else if (resourceType === "Coverage") {
// deidentify the coverage
let coverage = deidentifyCoverage(resource.resource);
// replace the coverage resource with the deidentified version
resource.resource = coverage
}
})
});

// set the prefetch reference to the modified copy
prefetch = newPrefetch
}

const r4json = {
"hookInstance": "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
"fhirServer": ehrUrl,
Expand Down
39 changes: 39 additions & 0 deletions src/util/deidentifyCoverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

export default function deidentifyCoverage(coverage) {
let newCoverage = {};
const profile = "http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-coverage-deident";
const metaKey = "meta";
const profileKey = "profile";

for (const key in coverage) {
// remove: text, identifier, policyHolder, subscriber, subscriberId, dependent, relationship, order, network, costToBeneficiary, subrogation, contract
if ((key !== "text") && (key !== "identifier") && (key !== "policyHolder") && (key !== "subscriber") && (key !== "subscriberId") && (key !== "dependent") && (key !== "relationship") && (key !== "order") && (key !== "network") && (key !== "costToBeneficiary") && (key !== "subrogation") && (key !== "contract")) {
if (key === metaKey) {
// copy meta if there is one
let meta = coverage[key];

if (profileKey in meta) {
// make sure the profile is not already in the list
if (!meta[profileKey].includes(profile)) {
// append to the existing profile list
meta[profileKey].push(profile);
}
} else {
// add the profile to meta
meta[profileKey] = [ profile ];
}
newCoverage[key] = meta
}
else {
newCoverage[key] = coverage[key];
}
}
}

// if no meta, add it with the profile
if (!(metaKey in newCoverage)) {
newCoverage[metaKey] = { [profileKey]: [ profile ] };
}

return newCoverage;
}
75 changes: 75 additions & 0 deletions src/util/deidentifyPatient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@

export default function deidentifyPatient(patient) {
let newPatient = {};
const profile = "http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-patient-deident";
const metaKey = "meta";
const profileKey = "profile";

for (const key in patient) {
// remove: text, identifier, name, telecom, deceased, multipleBirth, photo, contact, link
if ((key !== "text") && (key !== "identifier") && (key !== "name") && (key !== "telecom") && (key !== "deceased") && (key !== "multipleBirth") && (key !== "photo") && (key !== "contact") && (key !== "link")) {
// handle birthDate
if (key === "birthDate") {
newPatient[key] = processDate(patient[key]);
}
else if (key === "address") {
// remove the following from address: text, line, city, district, postalCode, period
let addresses = patient[key];
let newAddresses = [];
for (const addrIndex in addresses) {
let address = addresses[addrIndex];
let newAddress = {};
for (const addrKey in address) {
if ((addrKey !== "text") && (addrKey !== "line") && (addrKey !== "city") && (addrKey !== "district") && (addrKey !== "postalCode") && (addrKey !== "period")) {
newAddress[addrKey] = address[addrKey];
}
}
newAddresses.push(newAddress);
}
newPatient[key] = newAddresses;
}
else if (key === metaKey) {
// copy meta if there is one
let meta = patient[key];

if (profileKey in meta) {
// make sure the profile is not already in the list
if (!meta[profileKey].includes(profile)) {
// append to the existing profile list
meta[profileKey].push(profile);
}
} else {
// add the profile to meta
meta[profileKey] = [ profile ];
}
newPatient[key] = meta
}
else {
newPatient[key] = patient[key];
}
}
}

// if no meta, add it with the profile
if (!(metaKey in newPatient)) {
newPatient[metaKey] = { [profileKey]: [ profile ] };
}

return newPatient;
}

function processDate(dateString) {
let date = new Date(dateString);
const today = new Date();
const diffTime = Math.abs(today - date);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

// just the year
let newDateString = date.toISOString().substring(0,4);

// if less than two years old include the month
if (diffDays < (2 * 365)) {
newDateString = date.toISOString().substring(0,7);
}
return newDateString;
}