Skip to content

Commit

Permalink
Merged in add-fhir-r4 (pull request #10)
Browse files Browse the repository at this point in the history
Add FHIR R4 Support

Approved-by: Noranda Brown <noranda@norandabrown.com>
  • Loading branch information
cmoesel authored and noranda committed Feb 6, 2020
2 parents 5f391ec + 2d7f986 commit 670320c
Show file tree
Hide file tree
Showing 34 changed files with 16,394 additions and 333 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
/build
/coverage
/tx_bundle_dump
/test_patient_dump
*.css
5 changes: 2 additions & 3 deletions .rescriptsrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,10 @@ const stubUnneededFiles = config => {
// Replace cql-execution's packaged FHIR model w/ a stubbed version since we
// don't use the FHIR model from cql-execution and it takes up a lot of space.
resolve.alias['./fhir/models'] = path.resolve(__dirname, './src/stubs/fhir-models.js');
// Replace cql-exec-fhir's bundled 1.6, 3.0.0, and 4.0.0 modelinfos with stubs since we
// only use the 1.0.2 modelinfo and the others take up a lot of space.
// Replace cql-exec-fhir's bundled 1.6, and 3.0.0 modelinfos with stubs since we
// only use the 1.0.2 and 4.0.0 modelinfos and the others take up a lot of space.
resolve.alias['./modelInfos/fhir-modelinfo-1.6.xml.js'] = path.resolve(__dirname, './src/stubs/fhir-modelinfo-stub.xml.js');
resolve.alias['./modelInfos/fhir-modelinfo-3.0.0.xml.js'] = path.resolve(__dirname, './src/stubs/fhir-modelinfo-stub.xml.js');
resolve.alias['./modelInfos/fhir-modelinfo-4.0.0.xml.js'] = path.resolve(__dirname, './src/stubs/fhir-modelinfo-stub.xml.js');
return resolve;
},
[['resolve']],
Expand Down
68 changes: 21 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The Pain Management Summary SMART on FHIR application was developed to support t

The Pain Management Summary SMART on FHIR application was piloted during Summer 2018. Local modifications and development were needed to fully support this application in the pilot environment. For example, custom development was needed to expose pain assessments via the FHIR API. See the pilot reports for more information.

This application was originally piloted with support for FHIR DSTU2. The app has been updated since the pilot to also support FHIR R4, although pilot R4 support has not been piloted in a clinical setting.

This prototype application is part of the [CDS Connect](https://cds.ahrq.gov/cdsconnect) project, sponsored by the [Agency for Healthcare Research and Quality](https://www.ahrq.gov/) (AHRQ), and developed under contract with AHRQ by [MITRE's CAMH](https://www.mitre.org/centers/cms-alliances-to-modernize-healthcare/who-we-are) FFRDC.

## Contributions
Expand All @@ -23,6 +25,7 @@ Once the necessary FHIR data has been retrieved from the EHR, the open source [C
### Limitations

This CDS logic queries for several concepts that do not yet have standardized codes. To support this, the following local codes have been defined:

| Code | System | Display |
| --- | --- | --- |
| PEGASSESSMENT | http://cds.ahrq.gov/cdsconnect/pms | Pain Enjoyment General Activity (PEG) Assessment |
Expand All @@ -42,8 +45,9 @@ Systems integrating the Pain Management Summary will need to expose the correspo
2. Install [Yarn](https://yarnpkg.com/en/docs/install) (1.3.x or above)
3. Install dependencies by executing `yarn` from the project's root directory
4. If you have a SMART-on-FHIR client ID, edit `public/launch-context.json` to specify it
5. If you'll be launching the app from an Epic EHR, modify `.env` to set `REACT_APP_EPIC_SUPPORTED_QUERIES` to `true`
6. Serve the code by executing `yarn start` (runs on port 8000)
5. NOTE: The launch context contains `"completeInTarget": true`. This is needed if you are running in an environment that initializes the app in a separate window (such as the public SMART sandbox). It can be safely removed in other cases.
6. If you'll be launching the app from an Epic EHR, modify `.env` to set `REACT_APP_EPIC_SUPPORTED_QUERIES` to `true`
7. Serve the code by executing `yarn start` (runs on port 8000)

## To build and deploy using a standard web server (static HTML and JS)

Expand All @@ -55,12 +59,13 @@ The Pain Management Summary can be deployed as static web resources on any HTTP
4. Modify the `homepage` value in `package.json` to reflect the path (after the hostname) at which it will be deployed
a. For example, if deploying to https://my-server/pain-mgmt-summary/, the `homepage` value should be `"http://localhost:8000/pain-mgmt-summary"` (note that the hostname need not match)
b. If deploying to the root of the domain, you can leave `homepage` as `"."`
5. Modify the `client_id` in `public/launch-context.json` to match the unique client ID you registered with the EHR from which this app will be launched
6. If you've set up an analytics endpoint (see below), set the `analytics_endpoint` and `x_api_key` in `public/config.json`
7. If you'll be launching the app from an Epic EHR, modify `.env` to set `REACT_APP_EPIC_SUPPORTED_QUERIES` to `true`
5. Modify the `clientId` in `public/launch-context.json` to match the unique client ID you registered with the EHR from which this app will be launched
6. NOTE: The launch context contains `"completeInTarget": true`. This is needed if you are running in an environment that initializes the app in a separate window (such as the public SMART sandbox). It can be safely removed in other cases.
7. If you've set up an analytics endpoint (see below), set the `analytics_endpoint` and `x_api_key` in `public/config.json`
8. If you'll be launching the app from an Epic EHR, modify `.env` to set `REACT_APP_EPIC_SUPPORTED_QUERIES` to `true`
a. This modifies some queries based on Epic-specific requirements
8. Run `yarn build` to compile the code to static files in the `build` folder
9. Deploy the output from the `build` folder to a standard web server
9. Run `yarn build` to compile the code to static files in the `build` folder
10. Deploy the output from the `build` folder to a standard web server

Optionally to step 9, you can run the static build contents in a simple Node http-server via the command: `yarn start-static`.

Expand All @@ -75,13 +80,14 @@ To execute the unit tests:
Run the app via one of the options above, then:

1. Browse to http://launch.smarthealthit.org/
2. In the _App Launch URL_ box at the bottom of the page, enter: `http://localhost:8000/launch.html`
3. Click _Launch App!_
4. Select a patient
2. Select `R2 (DSTU2)` or `R4` from the FHIR Version dropdown
3. In the _App Launch URL_ box at the bottom of the page, enter: `http://localhost:8000/launch.html`
4. Click _Launch App!_
5. Select a patient

### To upload test patients to the public SMART sandbox

Testing this SMART App is more meaningful when we can supply test patients that exercise various aspects of the application. Test patients are represented as FHIR bundles at `src/utils/test_patients`. To upload the test patients to the public SMART sandbox:
Testing this SMART App is more meaningful when we can supply test patients that exercise various aspects of the application. Test patients are represented as FHIR bundles at `src/utils/dstu2_test_patients` and `r4_test_patients`. To upload the test patients to the public SMART sandbox:

1. Run `yarn upload-test-patients`

Expand All @@ -94,53 +100,21 @@ The SMART launcher has a bug that doesn't allow IE 11 to enter the launch URL.
1. Overwrite the `/public/launch-context.json` file with these contents:
```json
{
"client": {
"client_id": "6c12dff4-24e7-4475-a742-b08972c4ea27",
"scope": "patient/*.read launch/patient"
},
"server": "url-goes-here"
"clientId": "6c12dff4-24e7-4475-a742-b08972c4ea27",
"scope": "patient/*.read launch/patient",
"iss": "url-goes-here"
}
```
2. Restart the application server
3. Browse to http://launch.smarthealthit.org/
4. Select `R2 (DSTU2)` from the FHIR Version dropdown
4. Select `R2 (DSTU2)` or `R4` from the FHIR Version dropdown
5. In _Launch Type_, choose **Provider Standalone Launch**
6. Copy the FHIR URL in the _FHIR Server URL_ box at the bottom of the page (e.g., `http://launch.smarthealthit.org/v/r2/sim/eyJoIjoiMSIsImkiOiIxIiwiaiI6IjEifQ/fhir`)
7. Paste it into `/public/launch-context.json` file where `url-goes-here` is
8. Browse to http://localhost:8000/launch.html

_NOTE: Do *not* check in the modified launch-context.json!_

## To test the app using a local instance of the SMART Platform

First install the SMART Platform via: https://github.com/smart-on-fhir/installer

Verify it works via the sample apps included with it:
1. Browse to http://localhost:9080/
2. Sign in using demo/demo
3. Pick "SMART DSTU2 Sandbox"
4. Click "Growth Chart" app
5. Click "Launch"
6. Click "Clark, Susan A."

Then run the app via one of the options above and register it via the SMART Platform:

1. Browse to http://localhost:9080/ (if not already signed in)
2. Sign in using demo/demo (if not already signed in)
3. Pick "SMART DSTU2 Sandbox" (if not already signed in)
4. Choose "Register Manually"
5. Enter these values:
a. App Type: Public Client
b. App Name: CQL Demo
c. App Launch URI: http://localhost:8000/launch.html
d. App Redirect URIs: http://localhost:8000/
e. Allow Offline Access: NO
f. Patient Scoped App: YES
6. Save
7. Click the "CQL Demo" app
8. Click "Launch"
9. Choose a patient

## To test the app using the Epic SMART sandbox

The public Epic sandbox does not provide any synthetic patients that exercise the Pain Management Summary logic very well. For this reason, testing against the public Epic sandbox is generally only useful to prove basic connection capability.
Expand Down
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@
"homepage": "https://ahrq-cds.github.io/AHRQ-CDS-Connect-PAIN-MANAGEMENT-SUMMARY",
"license": "Apache-2.0",
"dependencies": {
"@babel/polyfill": "7.0.0-beta.46",
"@babel/polyfill": "^7.8.3",
"@fortawesome/fontawesome-svg-core": "^1.2.26",
"@fortawesome/free-solid-svg-icons": "^5.12.0",
"@fortawesome/react-fontawesome": "^0.1.8",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"cql-exec-fhir": "^1.3.0",
"cql-exec-fhir": "^1.3.1",
"cql-execution": "^1.3.7",
"fhirclient": "^0.1.15",
"moment": "^2.22.1",
"fhirclient": "^2.3.0",
"moment": "^2.24.0",
"react": "^16.12.0",
"react-collapsible": "^2.2.0",
"react-collapsible": "^2.6.2",
"react-dom": "^16.12.0",
"react-modal": "^3.4.4",
"react-router-dom": "^4.2.2",
"react-modal": "^3.11.1",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.0",
"react-table": "^6.8.2",
"react-tooltip": "^3.5.1",
"tocbot": "^4.3.0"
"react-table": "^6.11.4",
"react-tooltip": "^3.11.6",
"tocbot": "^4.10.0"
},
"devDependencies": {
"@rescripts/cli": "^0.0.13",
Expand Down
50 changes: 44 additions & 6 deletions public/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
# Factors to Consider in Managing Chronic Pain
# Pain Management Summary SMART on FHIR Application

This is the SMART-on-FHIR app for the CDS artifact: "Factors to Consider in Managing Chronic Pain".
This app uses FHIR to retrieve patient data, executes CQL CDS logic against it, and displays the
results.
## About

The Pain Management Summary SMART on FHIR application was developed to support the pilot of the CDS artifact, [Factors to Consider in Managing Chronic Pain: A Pain Management Summary](https://cds.ahrq.gov/cdsconnect/artifact/factors-consider-managing-chronic-pain-pain-management-summary). This artifact presents a variety of key "factors" for clinicians to consider when assessing the history of a patient's chronic pain. These factors include subjective and objective findings, along with recorded treatments and interventions to inform shared decision making on treatments moving forward.

The Pain Management Summary SMART on FHIR application was piloted during Summer 2018. Local modifications and development were needed to fully support this application in the pilot environment. For example, custom development was needed to expose pain assessments via the FHIR API. See the pilot reports for more information.

This application was originally piloted with support for FHIR DSTU2. The app has been updated since the pilot to also support FHIR R4, although pilot R4 support has not been piloted in a clinical setting.

This prototype application is part of the [CDS Connect](https://cds.ahrq.gov/cdsconnect) project, sponsored by the [Agency for Healthcare Research and Quality](https://www.ahrq.gov/) (AHRQ), and developed under contract with AHRQ by [MITRE's CAMH](https://www.mitre.org/centers/cms-alliances-to-modernize-healthcare/who-we-are) FFRDC.

## Contributions

For information about contributing to this project, please see [CONTRIBUTING](CONTRIBUTING.md).

## Development Details

The Pain Management Summary is a web-based application implemented with the popular [React](https://reactjs.org/) JavaScript framework. The application adheres to the [SMART on FHIR](https://smarthealthit.org/) standard, allowing it to be integrated into EHR products that support the SMART on FHIR platform. To ensure the best adherence to the standard, the Pain Management Summary application uses the open source [FHIR client](https://github.com/smart-on-fhir/client-js) library provided by the SMART Health IT group.

The logic used to determine what data to display in the Pain Management Summary is defined using [CQL](http://cql.hl7.org/) and integrated into the application as the corresponding JSON ELM representation of the CQL. The application analyzes the JSON ELM representation to determine what data is needed and then makes the corresponding queries to the FHIR server.

Once the necessary FHIR data has been retrieved from the EHR, the open source [CQL execution engine](https://github.com/cqframework/cql-execution) library is invoked with it and the JSON ELM to calculate the structured summary of the data to display to the user. This structured summary is then used by the React components to render a user-friendly view of the information.

### Limitations

This CDS logic queries for several concepts that do not yet have standardized codes. To support this, the following local codes have been defined:

| Code | System | Display |
| --- | --- | --- |
| PEGASSESSMENT | http://cds.ahrq.gov/cdsconnect/pms | Pain Enjoyment General Activity (PEG) Assessment |
| PEGPAIN | http://cds.ahrq.gov/cdsconnect/pms | Pain |
| PEGENJOYMENT | http://cds.ahrq.gov/cdsconnect/pms | Enjoyment of life |
| PEGGENERALACTIVITY | http://cds.ahrq.gov/cdsconnect/pms | General activity |
| STARTBACK | http://cds.ahrq.gov/cdsconnect/pms | STarT Back Screening Tool |
| SQETOHUSE | http://cds.ahrq.gov/cdsconnect/pms | Single question r/t ETOH use |
| SQDRUGUSE | http://cds.ahrq.gov/cdsconnect/pms | Single question r/t drug use |
| MME | http://cds.ahrq.gov/cdsconnect/pms | Morphine Milligram Equivalent (MME) |

Systems integrating the Pain Management Summary will need to expose the corresponding data as observations using the codes above. As standardized codes become available, these local codes will be replaced.

## To change the client ID

Expand All @@ -12,11 +47,14 @@ by editing the client ID in the `launch-context.json` file:

```json
{
"client_id": "6c12dff4-24e7-4475-a742-b08972c4ea27",
"scope": "patient/*.read"
"clientId": "6c12dff4-24e7-4475-a742-b08972c4ea27",
"scope": "patient/*.read",
"completeInTarget": true
}
```

_NOTE: The launch context contains `"completeInTarget": true`. This is needed if you are running in an environment that initializes the app in a separate window (such as the public SMART sandbox). It can be safely removed in other cases._

## To configure analytics reporting

This app can post JSON-formatted analytic data to an endpoint each time the application is invoked.
Expand Down
5 changes: 3 additions & 2 deletions public/launch-context.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"client_id": "6c12dff4-24e7-4475-a742-b08972c4ea27",
"scope": "patient/*.read launch/patient"
"clientId": "6c12dff4-24e7-4475-a742-b08972c4ea27",
"scope": "patient/*.read launch/patient",
"completeInTarget": true
}
3 changes: 3 additions & 0 deletions scripts/serve-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ app.listen(8000, err => {
if (err != null) {
console.error(err);
}
console.log('NOTE: This is a development server intended to test static deployment of the')
console.log('Pain Management Summary. This server is not intended for production use.')
console.log();
console.log('Launch URL: http://localhost:8000/AHRQ-CDS-Connect-PAIN-MANAGEMENT-SUMMARY/launch.html');
});
2 changes: 1 addition & 1 deletion src/__tests__/components/FhirQuery.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import FhirQuery from '../../components/FhirQuery';

const component = shallowRender(FhirQuery, {
url: 'testUrl',
query: {}
data: {}
});

it('renders without crashing', () => {
Expand Down
9 changes: 8 additions & 1 deletion src/__tests__/components/Landing.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { shallowRender } from '../../utils/testHelpers';
import Landing from '../../components/Landing';

const component = shallowRender(Landing, {});
let spy;
beforeAll(() => {
// This test logs an ugly error because FHIR is not initialized (as expected).
// Since we expect this error, we suppress console.error here
spy = jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
});
afterAll(() => spy.mockRestore());

it('renders without crashing', () => {
const component = shallowRender(Landing, {});
expect(component).toExist();
});
15 changes: 4 additions & 11 deletions src/components/DevTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,10 @@ export default class DevTools extends Component {
}

errorMessage(er, i) {
let msg = 'See query info below for details';
if (er.error.responseJSON) {
msg = er.error.responseJSON.ExceptionMessage || er.error.responseJSON.Message;
} else if (er.error.statusText) {
msg = er.error.statusText;
}

return (
<tr key={i}>
<td>{er.config.type}</td>
<td>{msg}</td>
<td>{er.type}</td>
<td>{er.error.message || er.error.statusText || 'No error message provided'}</td>
</tr>
);
}
Expand Down Expand Up @@ -77,9 +70,9 @@ export default class DevTools extends Component {
<h4>FHIR Queries <button onClick={this.toggleFhirQueries}>[show/hide]</button></h4>
<div style={{ display: this.state.displayFhirQueries ? 'block' : 'none' }}>
{this.props.collector.map((item, i) => {
const url = i === 0 ? item.config.url : item.config.url.slice(item.config.url.lastIndexOf('/') + 1);
const url = i === 0 ? item.url : item.url.slice(item.url.lastIndexOf('/') + 1);
return (
<FhirQuery key={i} url={url} query={item} />
<FhirQuery key={i} url={url} data={item.data} />
);
})}
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/components/FhirQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ export default class FhirQuery extends Component {
}

render() {
const { query, url } = this.props;
if (!query) { return null; }
const { data, url } = this.props;
if (!data) { return null; }

return (
<div className="fhir-query">
<b>{url}</b> <button href="#" onClick={this.toggleShowHide}>[show/hide]</button>
<pre style={{display: this.state.displayed ? 'block' : 'none'}}>{JSON.stringify(query.data, null, 2)}</pre>
<pre style={{display: this.state.displayed ? 'block' : 'none'}}>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
}

FhirQuery.propTypes = {
query: PropTypes.object.isRequired,
data: PropTypes.object.isRequired,
url: PropTypes.string.isRequired
};
Loading

0 comments on commit 670320c

Please sign in to comment.