Skip to content

Commit

Permalink
Merge pull request #123 from mcode/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
smalho01 authored Dec 4, 2023
2 parents cd7514b + 1126a64 commit 65d1cad
Show file tree
Hide file tree
Showing 14 changed files with 698 additions and 315 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ NOTE: The REMS Administrator is a work in progress.
## Running the REMS Adminstrator

#### Initialization
After cloning the repsistory, the submodules must be initialized. To do this you can run:
After cloning the repository, the submodules must be initialized. To do this you can run:

```
git submodule update --init
Expand All @@ -70,7 +70,7 @@ npm test
```
npm start
```
Application will be runnin on port 8090.
Application will be running on port 8090.
To reach the CDS Services discovery information:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"moment": "^2.24.0",
"moment-timezone": "^0.5.40",
"mongodb": "^4.12.1",
"mongoose": "7.0.3",
"mongoose": "7.3.3",
"morgan": "^1.9.1",
"uid": "^2.0.1",
"uuid": "^9.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/fhir/models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Document, Schema, model } from 'mongoose';

interface Medication extends Document {
export interface Medication extends Document {
name: string;
codeSystem: string;
code: string;
Expand Down
7 changes: 1 addition & 6 deletions src/fhir/questionnaireUtilities.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
Bundle,
BundleEntry,
DataRequirement,
Extension,
FhirResource,
Library,
Expand All @@ -10,10 +9,8 @@ import {
QuestionnaireItem,
ValueSet
} from 'fhir/r4';
import { FhirUtilities } from './utilities';
import container from '../lib/winston';
import config from '../config';
import axios from 'axios';
import ValueSetModel from '../lib/schemas/resources/ValueSet';
import QuestionnaireModel from '../lib/schemas/resources/Questionnaire';
import LibraryModel from '../lib/schemas/resources/Library';
Expand All @@ -29,7 +26,6 @@ interface LibraryMap {
[key: string]: Library;
}

const VSAC_CANONICAL_BASE = 'https://cts.nlm.nih.gov/fhir/ValueSet/';
const CQF_LIBRARY_EXTENSION = 'http://hl7.org/fhir/StructureDefinition/cqf-library';

export class QuestionnaireUtilities {
Expand All @@ -41,7 +37,6 @@ export class QuestionnaireUtilities {
const bundle: Bundle = { resourceType: 'Bundle', type: 'collection' };
const entries: BundleEntry[] = [];
const extensions: Extension[] = processedQuestionnaire.extension || [];
const fetchedSets: ValueSetMap = {};
const fetchedLibraries: LibraryMap = {};
for (const extension of extensions) {
if (extension.url === CQF_LIBRARY_EXTENSION) {
Expand Down Expand Up @@ -105,7 +100,7 @@ export class QuestionnaireUtilities {
}
// On load of new library, finds ValueSets in codefilters and
// loads them as well
static async processLibraryCodeFilters(library: Library, fetchedSets: ValueSetMap) {
static async processLibraryCodeFilters(library: Library) {
const returnValue = this.vsacCache.cacheLibrary(library);
return returnValue;
}
Expand Down
4 changes: 2 additions & 2 deletions src/fhir/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class FhirUtilities {
switch (resource.resourceType) {
case 'Library':
model = LibraryModel;
await QuestionnaireUtilities.processLibraryCodeFilters(resource, {});
await QuestionnaireUtilities.processLibraryCodeFilters(resource);
break;
case 'Patient':
model = PatientModel;
Expand Down Expand Up @@ -167,7 +167,7 @@ export class FhirUtilities {
requiredToDispense: true
},
{
name: 'Patient Status Update Form',
name: 'Patient Status Update',
description: 'Submit Patient Status Update form to the REMS Administrator',
stakeholderType: 'patient',
createNewCase: false,
Expand Down
31 changes: 28 additions & 3 deletions src/hooks/hookResources.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Coding } from 'fhir/r4';
import { MedicationRequest, Coding } from 'fhir/r4';
import { Link } from '../cards/Card';
import config from '../config';
import { Hook, TypedRequestBody } from '../rems-cds-hooks/resources/HookTypes';
import { TypedRequestBody } from '../rems-cds-hooks/resources/HookTypes';
import axios from 'axios';

export interface CardRule {
Expand Down Expand Up @@ -205,3 +204,29 @@ export function getFhirResource(token: string, req: TypedRequestBody) {
return e.data;
});
}

/*
* Retrieve the coding for the medication from the medicationCodeableConcept if available.
* Read coding from contained Medication matching the medicationReference otherwise.
*/
export function getDrugCodeFromMedicationRequest(medicationRequest: MedicationRequest) {
if (medicationRequest) {
if (medicationRequest?.medicationCodeableConcept) {
console.log('Get Medication code from CodeableConcept');
return medicationRequest?.medicationCodeableConcept?.coding?.[0];
} else if (medicationRequest?.medicationReference) {
const reference = medicationRequest?.medicationReference;
let coding = null;
medicationRequest?.contained?.every(e => {
if (e.resourceType + '/' + e.id === reference.reference) {
if (e.resourceType === 'Medication') {
console.log('Get Medication code from contained resource');
coding = e.code?.coding?.[0];
}
}
});
return coding;
}
}
return null;
}
34 changes: 23 additions & 11 deletions src/hooks/rems.orderselect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import { MedicationRequest } from 'fhir/r4';
import { Link } from '../cards/Card';
import config from '../config';
import { hydrate } from '../rems-cds-hooks/prefetch/PrefetchHydrator';
import { validCodes, codeMap, CARD_DETAILS, getFhirResource } from './hookResources';
import {
validCodes,
codeMap,
CARD_DETAILS,
getDrugCodeFromMedicationRequest
} from './hookResources';
import axios from 'axios';

interface TypedRequestBody extends Express.Request {
Expand Down Expand Up @@ -86,13 +91,17 @@ const handler = (req: TypedRequestBody, res: any) => {
const prefetchRequest = context.draftOrders?.entry?.[0].resource;
const practitioner = hydratedPrefetch?.practitioner;
const npi = practitioner?.identifier;
console.log(' Practitioner: ' + practitioner?.id + ' NPI: ' + npi);
console.log(' Patient: ' + patient?.id);

console.log(' MedicationRequest: ' + prefetchRequest?.id);
console.log(' Practitioner: ' + practitioner?.id + ' NPI: ' + npi);
console.log(' Patient: ' + patient?.id);

// verify there is a contextRequest
if (!contextRequest) {
res.json(buildErrorCard('DraftOrders does not contain a request'));
return;
}

// verify a MedicationRequest was sent
if (contextRequest && contextRequest.resourceType !== 'MedicationRequest') {
res.json(buildErrorCard('DraftOrders does not contain a MedicationRequest'));
Expand Down Expand Up @@ -125,7 +134,7 @@ const handler = (req: TypedRequestBody, res: any) => {
return;
}

const medicationCode = contextRequest?.medicationCodeableConcept?.coding?.[0];
const medicationCode = getDrugCodeFromMedicationRequest(contextRequest);
if (medicationCode && medicationCode.code) {
// find the drug in the medicationCollection to get the smart links
const drug = await medicationCollection
Expand Down Expand Up @@ -192,12 +201,13 @@ const handler = (req: TypedRequestBody, res: any) => {
smartLinkCount++;
}
} else {
// if (etasu)
// add all the links if no etasu to check
card.addLink(
createSmartLink(requirement.name, requirement.appContext, contextRequest)
);
smartLinkCount++;
// add all the required to dispense links if no etasu to check
if (requirement.requiredToDispense) {
card.addLink(
createSmartLink(requirement.name, requirement.appContext, contextRequest)
);
smartLinkCount++;
}
}
}
}
Expand All @@ -218,10 +228,12 @@ const handler = (req: TypedRequestBody, res: any) => {
res.json(buildErrorCard('MedicationRequest does not contain a code'));
}
}

console.log('REMS order-select hook');
try {
const fhirUrl = req.body.fhirServer;
if (fhirUrl) {
const fhirAuth = req.body.fhirAuthorization;
if (fhirUrl && fhirAuth && fhirAuth.access_token) {
hydrate(getFhirResource, hookPrefetch, req.body).then(
(hydratedPrefetch: OrderSelectPrefetch) => {
handleCard(hydratedPrefetch);
Expand Down
54 changes: 37 additions & 17 deletions src/hooks/rems.ordersign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import { MedicationRequest } from 'fhir/r4';
import { Link } from '../cards/Card';
import config from '../config';
import { hydrate } from '../rems-cds-hooks/prefetch/PrefetchHydrator';
import { validCodes, codeMap, CARD_DETAILS, getFhirResource } from './hookResources';
import {
validCodes,
codeMap,
CARD_DETAILS,
getDrugCodeFromMedicationRequest
} from './hookResources';
import axios from 'axios';

interface TypedRequestBody extends Express.Request {
Expand Down Expand Up @@ -41,7 +46,7 @@ function buildErrorCard(reason: string) {
}

const handler = (req: TypedRequestBody, res: any) => {
function getFhirResource(token: string) {
async function getFhirResource(token: string) {
const ehrUrl = `${req.body.fhirServer}/${token}`;
const access_token = req.body.fhirAuthorization?.access_token;
const options = {
Expand All @@ -50,10 +55,15 @@ const handler = (req: TypedRequestBody, res: any) => {
Authorization: `Bearer ${access_token}`
}
};
const response = axios(ehrUrl, options);
return response.then(e => {
return e.data;
});
// application errors out here if you can't reach out to the EHR and results in server stopping and subsequent requests failing
let response = { data: {} };
try {
response = await axios(ehrUrl, options);
} catch (error: any) {
console.warn('Could not connect to EHR Server: ' + error);
response = error;
}
return response?.data;
}

function createSmartLink(
Expand All @@ -80,13 +90,17 @@ const handler = (req: TypedRequestBody, res: any) => {
const prefetchRequest = hydratedPrefetch?.request;
const practitioner = hydratedPrefetch?.practitioner;
const npi = practitioner?.identifier;
console.log(' Practitioner: ' + practitioner?.id + ' NPI: ' + npi);
console.log(' Patient: ' + patient?.id);

console.log(' MedicationRequest: ' + prefetchRequest?.id);
console.log(' Practitioner: ' + practitioner?.id + ' NPI: ' + npi);
console.log(' Patient: ' + patient?.id);

// verify there is a contextRequest
if (!contextRequest) {
res.json(buildErrorCard('DraftOrders does not contain a request'));
return;
}

// verify a MedicationRequest was sent
if (contextRequest && contextRequest.resourceType !== 'MedicationRequest') {
res.json(buildErrorCard('DraftOrders does not contain a MedicationRequest'));
Expand Down Expand Up @@ -119,8 +133,11 @@ const handler = (req: TypedRequestBody, res: any) => {
return;
}

const medicationCode = contextRequest?.medicationCodeableConcept?.coding?.[0];
if (medicationCode && medicationCode.code) {
const medicationCode = getDrugCodeFromMedicationRequest(contextRequest);
if (!medicationCode) {
return;
}
if (medicationCode && medicationCode?.code) {
// find the drug in the medicationCollection to get the smart links
const drug = await medicationCollection
.findOne({
Expand Down Expand Up @@ -186,12 +203,13 @@ const handler = (req: TypedRequestBody, res: any) => {
smartLinkCount++;
}
} else {
// if (etasu)
// add all the links if no etasu to check
card.addLink(
createSmartLink(requirement.name, requirement.appContext, contextRequest)
);
smartLinkCount++;
// add all the required to dispense links if no etasu to check
if (requirement.requiredToDispense) {
card.addLink(
createSmartLink(requirement.name, requirement.appContext, contextRequest)
);
smartLinkCount++;
}
}
}
}
Expand All @@ -212,10 +230,12 @@ const handler = (req: TypedRequestBody, res: any) => {
res.json(buildErrorCard('MedicationRequest does not contain a code'));
}
}

console.log('REMS order-sign hook');
try {
const fhirUrl = req.body.fhirServer;
if (fhirUrl) {
const fhirAuth = req.body.fhirAuthorization;
if (fhirUrl && fhirAuth && fhirAuth.access_token) {
hydrate(getFhirResource, hookPrefetch, req.body).then(
(hydratedPrefetch: OrderSignPrefetch) => {
handleCard(hydratedPrefetch);
Expand Down
Loading

0 comments on commit 65d1cad

Please sign in to comment.