From 6155ce2ffd087b69ec1185afc0b09056db748a0f Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 6 Jun 2024 13:35:44 -0400 Subject: [PATCH 01/20] Prefix possibly unused variable --- src/hooks/hookResources.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 3253535..dfd7b23 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -550,7 +550,7 @@ function processMedicationRequests(medicationRequestsBundle: Bundle) { export async function handleCardEncounter( res: any, hookPrefetch: HookPrefetch | undefined, - contextRequest: FhirResource | undefined, + _contextRequest: FhirResource | undefined, patient: FhirResource | undefined ) { //TODO: should we add the other pdf information links to the card, or just have the smart links? From 34a17461f5342aaa714b591c706c4b835e7e730b Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 6 Jun 2024 14:37:34 -0400 Subject: [PATCH 02/20] Light refactoring --- src/hooks/hookResources.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index dfd7b23..567b273 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -2,7 +2,7 @@ import { MedicationRequest, Coding, FhirResource, Task, Patient, Bundle } from ' import Card, { Link, Suggestion, Action } from '../cards/Card'; import { HookPrefetch, TypedRequestBody } from '../rems-cds-hooks/resources/HookTypes'; import config from '../config'; -import { Requirement, medicationCollection, remsCaseCollection } from '../fhir/models'; +import { RemsCase, Requirement, medicationCollection, remsCaseCollection } from '../fhir/models'; import axios from 'axios'; import { ServicePrefetch } from '../rems-cds-hooks/resources/CdsService'; @@ -546,6 +546,10 @@ function processMedicationRequests(medicationRequestsBundle: Bundle) { }); } +const getSummary = (rule: CardRule | undefined, remsCase: RemsCase | undefined) => { + return rule?.summary || remsCase?.drugName || 'Rems'; +}; + // handles order-sign and order-select currently export async function handleCardEncounter( res: any, @@ -553,8 +557,6 @@ export async function handleCardEncounter( _contextRequest: FhirResource | undefined, patient: FhirResource | undefined ) { - //TODO: should we add the other pdf information links to the card, or just have the smart links? - const medResource = hookPrefetch?.medicationRequests; const medicationRequestsBundle = medResource?.resourceType === 'Bundle' ? medResource : undefined; @@ -571,6 +573,7 @@ export async function handleCardEncounter( }); // loop through all the rems cases in the list + for (const remsCase of remsCaseList) { // find the drug in the medicationCollection that matches the REMS case to get the smart links const drug = await medicationCollection @@ -582,12 +585,10 @@ export async function handleCardEncounter( // get the rule summary from the codemap const codeRule = codeMap[remsCase.drugCode]; - let summary = ''; - for (const rule of codeRule) { - if (rule.stakeholderType === 'patient') { - summary = rule.summary || remsCase.drugName || 'Rems'; - } - } + console.log('codeRule', JSON.stringify(codeRule)); + + const rule = codeRule.find(rule => rule.stakeholderType === 'patient'); + const summary = getSummary(rule, remsCase); // create the card let smartLinkCount = 0; From 03f43dafa00345870efb16da08ea8f5ec5c5b697 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 6 Jun 2024 14:59:29 -0400 Subject: [PATCH 03/20] Rewrite patient-view and encounter-start callback to functional programming style --- src/hooks/hookResources.ts | 49 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 567b273..115ecfb 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -591,7 +591,6 @@ export async function handleCardEncounter( const summary = getSummary(rule, remsCase); // create the card - let smartLinkCount = 0; const card = new Card(summary, CARD_DETAILS, source, 'info'); // process the MedicationRequests to add the Medication into contained resources @@ -617,34 +616,36 @@ export async function handleCardEncounter( // loop through all of the ETASU requirements for this drug const requirements = drug?.requirements || []; - for (const requirement of requirements) { - // find all of the matching patient forms - if (requirement?.stakeholderType === 'patient') { - let found = false; - // match the requirement to the metRequirement of the REMS case - for (const metRequirement of remsCase.metRequirements) { - // only add the link if the form is still needed to be completed - if (metRequirement.requirementName === requirement.name) { - found = true; - if (!metRequirement.completed) { - card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); - smartLinkCount++; - } + const smartLinks = requirements + .map(requirement => { + // find all of the matching patient forms + if (requirement?.stakeholderType === 'patient') { + // match the requirement to the metRequirement of the REMS case + const metRequirement = remsCase.metRequirements.find(metRequirement => { + return metRequirement.requirementName === requirement.name; + }); + + const link = createSmartLink(requirement.name, requirement.appContext, request); + + if ( + // add the link if the form is still needed to be completed + (!!metRequirement && !metRequirement.completed) || + // if not in the list of metRequirements, add it as well + !metRequirement + ) { + return link; } + return []; } + return []; + }) + .flat(); - // if not in the list of metRequirements, add it as well - if (!found) { - card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); - smartLinkCount++; - } - } + for (const link of smartLinks) { + card.addLink(link); } - // only add the card to the list if there is a link - if (smartLinkCount > 0) { - cardArray.push(card); - } + cardArray.push(card); } res.json({ From 5fba23c1eeff795812c0a2ff2934d6b7c5d5cbd6 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 6 Jun 2024 15:26:12 -0400 Subject: [PATCH 04/20] Add in refactored code from code review of encounter-start support PR --- src/hooks/hookResources.ts | 147 +++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 48 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 115ecfb..0952e53 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -1,4 +1,13 @@ -import { MedicationRequest, Coding, FhirResource, Task, Patient, Bundle } from 'fhir/r4'; +import { + MedicationRequest, + Coding, + FhirResource, + Task, + Patient, + Bundle, + Medication, + BundleEntry +} from 'fhir/r4'; import Card, { Link, Suggestion, Action } from '../cards/Card'; import { HookPrefetch, TypedRequestBody } from '../rems-cds-hooks/resources/HookTypes'; import config from '../config'; @@ -504,50 +513,93 @@ export function handleHook( } // process the MedicationRequests to add the Medication into contained resources -function processMedicationRequests(medicationRequestsBundle: Bundle) { - medicationRequestsBundle?.entry?.forEach(entry => { - if (entry?.resource?.resourceType === 'MedicationRequest') { - if (entry?.resource?.medicationReference) { - const medicationReference = entry?.resource?.medicationReference; - medicationRequestsBundle?.entry?.forEach(e => { - if (e?.resource?.resourceType === 'Medication') { - if ( - e?.resource?.resourceType + '/' + e?.resource?.id === - medicationReference?.reference - ) { - if (entry) { - if (entry.resource) { - const reference = e?.resource; - const request = entry.resource as MedicationRequest; - - // add the reference as a contained resource to the request - if (!request?.contained) { - request.contained = []; - request.contained.push(reference); - } else { - // only add to contained if not already in there - let found = false; - request.contained.forEach(c => { - if (c.id === reference.id) { - found = true; - } - }); - if (!found) { - request.contained.push(reference); - } - } - } - } - } - } - }); +const refersToMedication = (entry: BundleEntry): boolean => + entry.resource?.resourceType === 'Medication'; + +const refersToMedicationRequest = (entry: BundleEntry): boolean => + entry.resource?.resourceType === 'MedicationRequest'; + +const refersToMedicationWithMedicationReference = (e: BundleEntry): boolean => + !!e.resource?.medicationReference; + +const isBundleEntryMedicationReferenced = + (medicationRequestEntry: BundleEntry) => + (medicationEntry: BundleEntry): boolean => + medicationEntry?.resource?.resourceType + '/' + medicationEntry?.resource?.id === + medicationRequestEntry.resource?.medicationReference?.reference; + +const createBundleEntryWhoseMedicationRequestContainsReferencedMedication = + (medicationEntries: BundleEntry[]) => + (medicationRequestEntry: BundleEntry): BundleEntry => { + if (!medicationRequestEntry.resource) { + return medicationRequestEntry; + } + const referencedMedication: Medication = medicationEntries.find( + isBundleEntryMedicationReferenced(medicationRequestEntry) + )?.resource!; + const contained = getContained(medicationRequestEntry, referencedMedication); + const mutatedMedicationRequestEntry: BundleEntry = { + ...medicationRequestEntry, + resource: { + ...medicationRequestEntry.resource, + contained } + }; + return mutatedMedicationRequestEntry; + }; + +function getContained( + medicationRequestEntry: BundleEntry, + referencedMedication: Medication +) { + const existingContained = medicationRequestEntry.resource?.contained; + if (existingContained) { + const foundReferencedMedication = existingContained.find( + c => c.id === referencedMedication?.id + ); + if (foundReferencedMedication || !referencedMedication) { + return existingContained; } - }); + return [...existingContained, referencedMedication]; + } + return [referencedMedication]; +} + +function processMedicationRequests( + medicationRequestsBundle: Bundle | undefined +): Bundle | undefined { + if (!medicationRequestsBundle) { + return undefined; + } + const { entry = [], ...rest } = medicationRequestsBundle; + const medicationRequestEntries = entry.filter( + refersToMedicationRequest + ) as BundleEntry[]; + const medicationRequestEntriesWithMedicationReference = medicationRequestEntries.filter( + refersToMedicationWithMedicationReference + ); + const medicationEntries = entry.filter(refersToMedication) as BundleEntry[]; + const medicationRequestEntriesMutatedWithMedicationReference = + medicationRequestEntriesWithMedicationReference.map( + createBundleEntryWhoseMedicationRequestContainsReferencedMedication(medicationEntries) + ); + const otherEntries = entry.filter(e => !refersToMedication(e) && !refersToMedicationRequest(e)); + const medicationRequestEntriesWithoutMedicationReference = medicationRequestEntries.filter( + e => !refersToMedicationWithMedicationReference(e) + ); + return { + ...rest, + entry: [ + ...otherEntries, + ...medicationEntries, + ...medicationRequestEntriesWithoutMedicationReference, + ...medicationRequestEntriesMutatedWithMedicationReference + ] + }; } -const getSummary = (rule: CardRule | undefined, remsCase: RemsCase | undefined) => { - return rule?.summary || remsCase?.drugName || 'Rems'; +const getSummary = (rule: CardRule | undefined, drugName: string | undefined) => { + return rule?.summary || drugName || 'Rems'; }; // handles order-sign and order-select currently @@ -558,7 +610,11 @@ export async function handleCardEncounter( patient: FhirResource | undefined ) { const medResource = hookPrefetch?.medicationRequests; - const medicationRequestsBundle = medResource?.resourceType === 'Bundle' ? medResource : undefined; + const medicationRequestsBundle = + medResource?.resourceType === 'Bundle' + ? // process the MedicationRequests to add the Medication into contained resources + processMedicationRequests(medResource) + : undefined; // create empty card array const cardArray: Card[] = []; @@ -588,16 +644,11 @@ export async function handleCardEncounter( console.log('codeRule', JSON.stringify(codeRule)); const rule = codeRule.find(rule => rule.stakeholderType === 'patient'); - const summary = getSummary(rule, remsCase); + const summary = getSummary(rule, remsCase.drugName); // create the card const card = new Card(summary, CARD_DETAILS, source, 'info'); - // process the MedicationRequests to add the Medication into contained resources - if (medicationRequestsBundle) { - processMedicationRequests(medicationRequestsBundle); - } - // find the matching MedicationRequest for the context const request = medicationRequestsBundle?.entry?.find(entry => { if (entry.resource) { From fcc1ce20ba60ad6d3c6bc2e5cc77a58522e41492 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 6 Jun 2024 15:52:19 -0400 Subject: [PATCH 05/20] Convert giant for loop into more functional programming style code --- src/hooks/hookResources.ts | 168 ++++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 75 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 0952e53..624e1fa 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -11,7 +11,14 @@ import { import Card, { Link, Suggestion, Action } from '../cards/Card'; import { HookPrefetch, TypedRequestBody } from '../rems-cds-hooks/resources/HookTypes'; import config from '../config'; -import { RemsCase, Requirement, medicationCollection, remsCaseCollection } from '../fhir/models'; +import { + RemsCase, + Requirement, + medicationCollection, + remsCaseCollection, + Medication as MongooseMedication, + MetRequirements as MongooseMetRequirements +} from '../fhir/models'; import axios from 'axios'; import { ServicePrefetch } from '../rems-cds-hooks/resources/CdsService'; @@ -548,10 +555,10 @@ const createBundleEntryWhoseMedicationRequestContainsReferencedMedication = return mutatedMedicationRequestEntry; }; -function getContained( +const getContained = ( medicationRequestEntry: BundleEntry, referencedMedication: Medication -) { +): FhirResource[] => { const existingContained = medicationRequestEntry.resource?.contained; if (existingContained) { const foundReferencedMedication = existingContained.find( @@ -563,7 +570,7 @@ function getContained( return [...existingContained, referencedMedication]; } return [referencedMedication]; -} +}; function processMedicationRequests( medicationRequestsBundle: Bundle | undefined @@ -602,102 +609,113 @@ const getSummary = (rule: CardRule | undefined, drugName: string | undefined) => return rule?.summary || drugName || 'Rems'; }; -// handles order-sign and order-select currently -export async function handleCardEncounter( - res: any, - hookPrefetch: HookPrefetch | undefined, - _contextRequest: FhirResource | undefined, - patient: FhirResource | undefined -) { - const medResource = hookPrefetch?.medicationRequests; - const medicationRequestsBundle = - medResource?.resourceType === 'Bundle' - ? // process the MedicationRequests to add the Medication into contained resources - processMedicationRequests(medResource) - : undefined; - - // create empty card array - const cardArray: Card[] = []; - - // find all matching rems cases for the patient - const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; - const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; - const remsCaseList = await remsCaseCollection.find({ - patientFirstName: patientName?.given?.[0], - patientLastName: patientName?.family, - patientDOB: patientBirth - }); - - // loop through all the rems cases in the list +const containsMatchingMedicationRequest = + (drugCode: string) => + (entry: BundleEntry): boolean => { + if (entry.resource?.resourceType === 'MedicationRequest') { + const medReq: MedicationRequest = entry.resource; + const medicationCode = getDrugCodeFromMedicationRequest(medReq); + return drugCode === medicationCode?.code; + } + return false; + }; - for (const remsCase of remsCaseList) { +const getCard = + (entries: BundleEntry[] | undefined) => + async ({ drugCode, drugName, metRequirements }: RemsCase): Promise => { // find the drug in the medicationCollection that matches the REMS case to get the smart links const drug = await medicationCollection .findOne({ - code: remsCase.drugCode, - name: remsCase.drugName + code: drugCode, + name: drugName }) .exec(); // get the rule summary from the codemap - const codeRule = codeMap[remsCase.drugCode]; - console.log('codeRule', JSON.stringify(codeRule)); + const codeRule = codeMap[drugCode]; const rule = codeRule.find(rule => rule.stakeholderType === 'patient'); - const summary = getSummary(rule, remsCase.drugName); + const summary = getSummary(rule, drugName); // create the card const card = new Card(summary, CARD_DETAILS, source, 'info'); // find the matching MedicationRequest for the context - const request = medicationRequestsBundle?.entry?.find(entry => { - if (entry.resource) { - if (entry.resource.resourceType === 'MedicationRequest') { - const medReq: MedicationRequest = entry.resource; - const medicationCode = getDrugCodeFromMedicationRequest(medReq); - return remsCase.drugCode === medicationCode?.code; - } - } - })?.resource; + const request = (entries || []).find(containsMatchingMedicationRequest(drugCode))?.resource; // if no valid request or not a MedicationRequest found skip this REMS case if (!request || (request && request.resourceType !== 'MedicationRequest')) { - continue; + return []; } // loop through all of the ETASU requirements for this drug - const requirements = drug?.requirements || []; - const smartLinks = requirements - .map(requirement => { - // find all of the matching patient forms - if (requirement?.stakeholderType === 'patient') { - // match the requirement to the metRequirement of the REMS case - const metRequirement = remsCase.metRequirements.find(metRequirement => { - return metRequirement.requirementName === requirement.name; - }); - - const link = createSmartLink(requirement.name, requirement.appContext, request); - - if ( - // add the link if the form is still needed to be completed - (!!metRequirement && !metRequirement.completed) || - // if not in the list of metRequirements, add it as well - !metRequirement - ) { - return link; - } - return []; + const smartLinks = getSmartLinks(drug, metRequirements, request); + + card.addLinks(smartLinks); + + return card; + }; + +const getSmartLinks = ( + drug: MongooseMedication | null, + metRequirements: Partial[], + request: MedicationRequest +): Link[] => { + const requirements = drug?.requirements || []; + const smartLinks = requirements + .map(requirement => { + // find all of the matching patient forms + if (requirement?.stakeholderType === 'patient') { + // match the requirement to the metRequirement of the REMS case + const metRequirement = metRequirements.find(metRequirement => { + return metRequirement.requirementName === requirement.name; + }); + + const link = createSmartLink(requirement.name, requirement.appContext, request); + + if ( + // add the link if the form is still needed to be completed + (!!metRequirement && !metRequirement.completed) || + // if not in the list of metRequirements, add it as well + !metRequirement + ) { + return link; } return []; - }) - .flat(); + } + return []; + }) + .flat(); + return smartLinks; +}; - for (const link of smartLinks) { - card.addLink(link); - } +// handles order-sign and order-select currently +export async function handleCardEncounter( + res: any, + hookPrefetch: HookPrefetch | undefined, + _contextRequest: FhirResource | undefined, + patient: FhirResource | undefined +) { + const medResource = hookPrefetch?.medicationRequests; + const medicationRequestsBundle = + medResource?.resourceType === 'Bundle' + ? // process the MedicationRequests to add the Medication into contained resources + processMedicationRequests(medResource) + : undefined; - cardArray.push(card); - } + // find all matching rems cases for the patient + const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; + const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; + const remsCaseList = await remsCaseCollection.find({ + patientFirstName: patientName?.given?.[0], + patientLastName: patientName?.family, + patientDOB: patientBirth + }); + + // loop through all the rems cases in the list + const promises = remsCaseList.map(getCard(medicationRequestsBundle?.entry)); + + const cardArray = (await Promise.all(promises)).flat(); res.json({ cards: cardArray From 5e1820eddbf386fc71f52fafa7df4f2446468821 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 6 Jun 2024 16:48:57 -0400 Subject: [PATCH 06/20] Remove obsolete test --- test/rems.hook.test.ts | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 test/rems.hook.test.ts diff --git a/test/rems.hook.test.ts b/test/rems.hook.test.ts deleted file mode 100644 index 6f7f729..0000000 --- a/test/rems.hook.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -// import OrderSign from '../src/hooks/OrderSign'; -import getREMSHook from '../src/hooks/rems.orderselect'; -import { expect } from 'chai'; - -describe.skip('hook: test rems', () => { - it('should have definition and handler', () => { - /* - const prefetch = { - patient: 'Patient/{{context.patientId}}', - practitioner: 'Practitioner/{{context.userId}}' - }; - */ - // const expectedDefinition = new OrderSign( - // 'rems-order-sign', - // 'order-sign', - // 'REMS Requirement Lookup', - // 'REMS Requirement Lookup', - // prefetch - // ); - - // expect(getREMSHook).to.haveOwnProperty('definition'); - // expect(getREMSHook).to.haveOwnProperty('handler'); - - // expect(getREMSHook.definition).to.deep.equal(expectedDefinition); - expect(getREMSHook.handler).to.instanceOf(Function); - }); -}); From eb604ef03ffc65a116e31404c92defc5d601cfe1 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Wed, 12 Jun 2024 13:44:53 -0400 Subject: [PATCH 07/20] Refactor types used --- src/hooks/hookResources.ts | 51 +++++++++++++++++--------------- src/hooks/rems.encounterstart.ts | 8 +++-- src/hooks/rems.orderselect.ts | 9 ++++-- src/hooks/rems.ordersign.ts | 8 +++-- src/hooks/rems.patientview.ts | 8 +++-- src/rems-cds-hooks | 2 +- 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 624e1fa..1317f16 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -9,7 +9,11 @@ import { BundleEntry } from 'fhir/r4'; import Card, { Link, Suggestion, Action } from '../cards/Card'; -import { HookPrefetch, TypedRequestBody } from '../rems-cds-hooks/resources/HookTypes'; +import { + HookPrefetch, + TypedRequestBody, + TypedResponseBody +} from '../rems-cds-hooks/resources/HookTypes'; import config from '../config'; import { RemsCase, @@ -23,8 +27,9 @@ import { import axios from 'axios'; import { ServicePrefetch } from '../rems-cds-hooks/resources/CdsService'; import { hydrate } from '../rems-cds-hooks/prefetch/PrefetchHydrator'; + type HandleCallback = ( - res: any, + res: TypedResponseBody, hydratedPrefetch: HookPrefetch | undefined, contextRequest: FhirResource | undefined, patient: FhirResource | undefined @@ -312,7 +317,7 @@ export function buildErrorCard(reason: string) { // handles order-sign and order-select currently export async function handleCardOrder( - res: any, + res: TypedResponseBody, hydratedPrefetch: HookPrefetch | undefined, contextRequest: FhirResource | undefined, patient: FhirResource | undefined @@ -462,7 +467,7 @@ export async function handleCardOrder( // make sure code here is applicable to all supported hooks. export async function handleCard( req: TypedRequestBody, - res: any, + res: TypedResponseBody, hydratedPrefetch: HookPrefetch, contextRequest: FhirResource | undefined, callback: HandleCallback @@ -494,7 +499,7 @@ export async function handleCard( // handles all hooks, any supported hook should pass through this function export function handleHook( req: TypedRequestBody, - res: any, + res: TypedResponseBody, hookPrefetch: ServicePrefetch, contextRequest: FhirResource | undefined, callback: HandleCallback @@ -572,9 +577,9 @@ const getContained = ( return [referencedMedication]; }; -function processMedicationRequests( +const processMedicationRequests = ( medicationRequestsBundle: Bundle | undefined -): Bundle | undefined { +): Bundle | undefined => { if (!medicationRequestsBundle) { return undefined; } @@ -603,10 +608,13 @@ function processMedicationRequests( ...medicationRequestEntriesMutatedWithMedicationReference ] }; -} +}; -const getSummary = (rule: CardRule | undefined, drugName: string | undefined) => { - return rule?.summary || drugName || 'Rems'; +const getSummary = (drugCode: string, drugName: string): string => { + const codeRule = codeMap[drugCode]; + const rule = codeRule.find(rule => rule.stakeholderType === 'patient'); + const summary = rule?.summary || drugName || 'Rems'; + return summary; }; const containsMatchingMedicationRequest = @@ -632,10 +640,7 @@ const getCard = .exec(); // get the rule summary from the codemap - const codeRule = codeMap[drugCode]; - - const rule = codeRule.find(rule => rule.stakeholderType === 'patient'); - const summary = getSummary(rule, drugName); + const summary = getSummary(drugCode, drugName); // create the card const card = new Card(summary, CARD_DETAILS, source, 'info'); @@ -690,12 +695,12 @@ const getSmartLinks = ( }; // handles order-sign and order-select currently -export async function handleCardEncounter( - res: any, +export const handleCardEncounter = async ( + res: TypedResponseBody, hookPrefetch: HookPrefetch | undefined, _contextRequest: FhirResource | undefined, patient: FhirResource | undefined -) { +): Promise => { const medResource = hookPrefetch?.medicationRequests; const medicationRequestsBundle = medResource?.resourceType === 'Bundle' @@ -703,7 +708,7 @@ export async function handleCardEncounter( processMedicationRequests(medResource) : undefined; - // find all matching rems cases for the patient + // find all matching REMS cases for the patient const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; const remsCaseList = await remsCaseCollection.find({ @@ -712,15 +717,13 @@ export async function handleCardEncounter( patientDOB: patientBirth }); - // loop through all the rems cases in the list + // loop through all the REMS cases in the list const promises = remsCaseList.map(getCard(medicationRequestsBundle?.entry)); - const cardArray = (await Promise.all(promises)).flat(); + const cards = (await Promise.all(promises)).flat(); - res.json({ - cards: cardArray - }); -} + res.json({ cards }); +}; export function createQuestionnaireSuggestion( card: Card, diff --git a/src/hooks/rems.encounterstart.ts b/src/hooks/rems.encounterstart.ts index ca11070..8b56250 100644 --- a/src/hooks/rems.encounterstart.ts +++ b/src/hooks/rems.encounterstart.ts @@ -1,4 +1,8 @@ -import { EncounterStartHook, SupportedHooks } from '../rems-cds-hooks/resources/HookTypes'; +import { + EncounterStartHook, + SupportedHooks, + TypedResponseBody +} from '../rems-cds-hooks/resources/HookTypes'; import { ServicePrefetch, CdsService } from '../rems-cds-hooks/resources/CdsService'; import { handleCardEncounter, handleHook } from './hookResources'; @@ -20,7 +24,7 @@ const definition: CdsService = { prefetch: hookPrefetch }; -const handler = (req: TypedRequestBody, res: any) => { +const handler = (req: TypedRequestBody, res: TypedResponseBody) => { console.log('REMS encounter-start hook'); const contextRequest = undefined; handleHook(req, res, hookPrefetch, contextRequest, handleCardEncounter); diff --git a/src/hooks/rems.orderselect.ts b/src/hooks/rems.orderselect.ts index 9300c1b..6ba60e9 100644 --- a/src/hooks/rems.orderselect.ts +++ b/src/hooks/rems.orderselect.ts @@ -1,4 +1,8 @@ -import { SupportedHooks, OrderSelectHook } from '../rems-cds-hooks/resources/HookTypes'; +import { + SupportedHooks, + TypedResponseBody, + OrderSelectHook +} from '../rems-cds-hooks/resources/HookTypes'; import { ServicePrefetch, CdsService } from '../rems-cds-hooks/resources/CdsService'; import { handleCardOrder, handleHook } from './hookResources'; @@ -18,10 +22,11 @@ const definition: CdsService = { prefetch: hookPrefetch }; -const handler = (req: TypedRequestBody, res: any) => { +const handler = (req: TypedRequestBody, res: TypedResponseBody) => { console.log('REMS order-select hook'); const context = req.body.context; const selection = context.selections?.[0]; + // todo: type the non-generic typedrequestbody with fhir r4 bundle const contextRequest = context.draftOrders?.entry?.filter(entry => { if (entry.resource) { return selection === `${entry.resource.resourceType}/${entry.resource.id}`; diff --git a/src/hooks/rems.ordersign.ts b/src/hooks/rems.ordersign.ts index 3e0fedc..d15798a 100644 --- a/src/hooks/rems.ordersign.ts +++ b/src/hooks/rems.ordersign.ts @@ -1,4 +1,8 @@ -import { OrderSignHook, SupportedHooks } from '../rems-cds-hooks/resources/HookTypes'; +import { + OrderSignHook, + SupportedHooks, + TypedResponseBody +} from '../rems-cds-hooks/resources/HookTypes'; import { ServicePrefetch, CdsService } from '../rems-cds-hooks/resources/CdsService'; import { handleCardOrder, handleHook } from './hookResources'; @@ -18,7 +22,7 @@ const definition: CdsService = { prefetch: hookPrefetch }; -const handler = (req: TypedRequestBody, res: any) => { +const handler = (req: TypedRequestBody, res: TypedResponseBody) => { console.log('REMS order-sign hook'); const contextRequest = req.body.context.draftOrders?.entry?.[0]?.resource; handleHook(req, res, hookPrefetch, contextRequest, handleCardOrder); diff --git a/src/hooks/rems.patientview.ts b/src/hooks/rems.patientview.ts index 007e3ee..a3e9a6d 100644 --- a/src/hooks/rems.patientview.ts +++ b/src/hooks/rems.patientview.ts @@ -1,4 +1,8 @@ -import { PatientViewHook, SupportedHooks } from '../rems-cds-hooks/resources/HookTypes'; +import { + PatientViewHook, + SupportedHooks, + TypedResponseBody +} from '../rems-cds-hooks/resources/HookTypes'; import { ServicePrefetch, CdsService } from '../rems-cds-hooks/resources/CdsService'; import { handleCardEncounter, handleHook } from './hookResources'; @@ -20,7 +24,7 @@ const definition: CdsService = { prefetch: hookPrefetch }; -const handler = (req: TypedRequestBody, res: any) => { +const handler = (req: TypedRequestBody, res: TypedResponseBody) => { console.log('REMS patient-view hook'); const contextRequest = undefined; handleHook(req, res, hookPrefetch, contextRequest, handleCardEncounter); diff --git a/src/rems-cds-hooks b/src/rems-cds-hooks index 1bd0ffc..ca6a498 160000 --- a/src/rems-cds-hooks +++ b/src/rems-cds-hooks @@ -1 +1 @@ -Subproject commit 1bd0ffcbee4ee302160ff7b38093ee96aaf595ef +Subproject commit ca6a498c8086b604b59b2c8037a0a43a65574e7f From 9057ae2a9ad44bc013ee70e03dea6598379e63f1 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Wed, 12 Jun 2024 14:02:33 -0400 Subject: [PATCH 08/20] Fix linting issue --- src/hooks/hookResources.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 1317f16..69dd036 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -546,9 +546,9 @@ const createBundleEntryWhoseMedicationRequestContainsReferencedMedication = if (!medicationRequestEntry.resource) { return medicationRequestEntry; } - const referencedMedication: Medication = medicationEntries.find( + const referencedMedication = medicationEntries.find( isBundleEntryMedicationReferenced(medicationRequestEntry) - )?.resource!; + )?.resource; const contained = getContained(medicationRequestEntry, referencedMedication); const mutatedMedicationRequestEntry: BundleEntry = { ...medicationRequestEntry, @@ -562,7 +562,7 @@ const createBundleEntryWhoseMedicationRequestContainsReferencedMedication = const getContained = ( medicationRequestEntry: BundleEntry, - referencedMedication: Medication + referencedMedication: Medication | undefined ): FhirResource[] => { const existingContained = medicationRequestEntry.resource?.contained; if (existingContained) { @@ -574,6 +574,9 @@ const getContained = ( } return [...existingContained, referencedMedication]; } + if (!referencedMedication) { + return []; + } return [referencedMedication]; }; From b807ff060c1d9c79ad909d7118f9fc860f30fe60 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Wed, 12 Jun 2024 14:16:07 -0400 Subject: [PATCH 09/20] Update duplicated comment --- src/hooks/hookResources.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 69dd036..2807a9b 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -697,7 +697,7 @@ const getSmartLinks = ( return smartLinks; }; -// handles order-sign and order-select currently +// handles patient-view and encounter-start currently export const handleCardEncounter = async ( res: TypedResponseBody, hookPrefetch: HookPrefetch | undefined, From fc5a88e1ad92bd02bdaca118f7ae3ed0ee25efa8 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Wed, 12 Jun 2024 16:20:42 -0400 Subject: [PATCH 10/20] Get absolute links relevant to the patient --- src/hooks/hookResources.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 2807a9b..b6f61f4 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -620,6 +620,12 @@ const getSummary = (drugCode: string, drugName: string): string => { return summary; }; +const getAbsoluteLinks = (drugCode: string): Link[] => { + const codeRule = codeMap[drugCode]; + const rule = codeRule.find(rule => rule.stakeholderType === 'patient'); + return rule?.links || []; +}; + const containsMatchingMedicationRequest = (drugCode: string) => (entry: BundleEntry): boolean => { @@ -658,8 +664,11 @@ const getCard = // loop through all of the ETASU requirements for this drug const smartLinks = getSmartLinks(drug, metRequirements, request); + card.addLinks(Array.from(smartLinks)); - card.addLinks(smartLinks); + // grab absolute links relevant to the patient + const absoluteLinks = getAbsoluteLinks(drugCode); + card.addLinks(Array.from(absoluteLinks)); return card; }; @@ -670,6 +679,7 @@ const getSmartLinks = ( request: MedicationRequest ): Link[] => { const requirements = drug?.requirements || []; + const smartLinks = requirements .map(requirement => { // find all of the matching patient forms From 3129a3f7a43e5f731ab03d142d8c071919879335 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 13 Jun 2024 13:38:23 -0400 Subject: [PATCH 11/20] Refactor function --- src/hooks/hookResources.ts | 41 +++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index b6f61f4..023da7a 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -257,25 +257,30 @@ const source = { * 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; - } +export function getDrugCodeFromMedicationRequest( + resource: FhirResource | undefined +): Coding | null { + const medicationRequest = + resource?.resourceType === 'MedicationRequest' && (resource as MedicationRequest); + + if (!medicationRequest) { + return null; } + + if (medicationRequest.medicationCodeableConcept) { + return medicationRequest.medicationCodeableConcept?.coding?.[0] || null; + } + + if (medicationRequest.medicationReference) { + const reference = medicationRequest.medicationReference; + const medication = medicationRequest.contained?.find( + resource => + resource.resourceType + '/' + resource.id === reference.reference && + resource.resourceType === 'Medication' + ) as Medication; + return medication?.code?.coding?.[0] || null; + } + return null; } export function getFhirResource(token: string, req: TypedRequestBody) { From 64a4c3f685dad8a496e0f8f0652ec882b7d261c3 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 13 Jun 2024 14:01:46 -0400 Subject: [PATCH 12/20] More refactoring --- src/hooks/hookResources.ts | 243 ++++++++++++++++++------------------- 1 file changed, 121 insertions(+), 122 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 023da7a..7b77bda 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -320,26 +320,20 @@ export function buildErrorCard(reason: string) { return cards; } -// handles order-sign and order-select currently -export async function handleCardOrder( - res: TypedResponseBody, +const getErrorCard = ( hydratedPrefetch: HookPrefetch | undefined, - contextRequest: FhirResource | undefined, - patient: FhirResource | undefined -) { - const prefetchRequest = hydratedPrefetch?.request; - console.log(' MedicationRequest: ' + prefetchRequest?.id); - // verify there is a contextRequest + contextRequest: FhirResource | undefined +): { cards: Card[] } | null => { if (!contextRequest) { - res.json(buildErrorCard('DraftOrders does not contain a request')); - return; + return buildErrorCard('DraftOrders does not contain a request'); } - // verify a MedicationRequest was sent if (contextRequest && contextRequest.resourceType !== 'MedicationRequest') { - res.json(buildErrorCard('DraftOrders does not contain a MedicationRequest')); - return; + return buildErrorCard('DraftOrders does not contain a MedicationRequest'); } + + const prefetchRequest = hydratedPrefetch?.request; + if ( prefetchRequest?.id && contextRequest && @@ -347,125 +341,130 @@ export async function handleCardOrder( prefetchRequest.id.replace('MedicationRequest/', '') !== contextRequest.id.replace('MedicationRequest/', '') ) { - res.json(buildErrorCard('Context draftOrder does not match prefetch MedicationRequest ID')); - return; + return buildErrorCard('Context draftOrder does not match prefetch MedicationRequest ID'); } - const medicationCode = - contextRequest && - contextRequest.resourceType === 'MedicationRequest' && - getDrugCodeFromMedicationRequest(contextRequest); - if (!medicationCode) { + const medicationCode = getDrugCodeFromMedicationRequest(contextRequest)!; + if (!medicationCode?.code) { + return buildErrorCard('MedicationRequest does not contain a code'); + } + + const shouldReturnCard = validCodes.some(e => { + return e.code === medicationCode.code && e.system === medicationCode.system; + }); + if (!shouldReturnCard) { + return buildErrorCard('Unsupported code'); + } + + return null; +}; + +// handles order-sign and order-select currently +export async function handleCardOrder( + res: TypedResponseBody, + hydratedPrefetch: HookPrefetch | undefined, + contextRequest: FhirResource | undefined, + patient: FhirResource | undefined +) { + const errorCard = getErrorCard(hydratedPrefetch, contextRequest); + if (errorCard) { + res.json(errorCard); return; } - if (medicationCode && medicationCode?.code) { - // find the drug in the medicationCollection to get the smart links - const drug = await medicationCollection - .findOne({ - code: medicationCode.code, - codeSystem: medicationCode.system - }) - .exec(); - // count the total requirement for each type - - // find a matching rems case for the patient and this drug to only return needed results - const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; - const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; - const etasu = await remsCaseCollection.findOne({ - patientFirstName: patientName?.given?.[0], - patientLastName: patientName?.family, - patientDOB: patientBirth, - drugCode: medicationCode?.code - }); - - const returnCard = validCodes.some(e => { - return e.code === medicationCode.code && e.system === medicationCode.system; - }); - if (returnCard) { - const cardArray: Card[] = []; - const codeRule = codeMap[medicationCode.code]; - for (const rule of codeRule) { - const card = new Card( - rule.summary || medicationCode.display || 'Rems', - rule.cardDetails || CARD_DETAILS, - source, - 'info' - ); - rule.links.forEach(function (e) { - if (e.type === 'absolute') { - // no construction needed - card.addLink(e); - } - }); + // find the drug in the medicationCollection to get the smart links + const coding = !errorCard && getDrugCodeFromMedicationRequest(contextRequest)!; + const { code, system, display } = coding; + const request = coding && (contextRequest as MedicationRequest); + const drug = await medicationCollection + .findOne({ + code: code, + codeSystem: system + }) + .exec(); - let unmetRequirementSmartLinkCount = 0; - let smartLinkCount = 0; - - // process the smart links from the medicationCollection - // TODO: smart links should be built with discovered questionnaires, not hard coded ones - if (drug) { - for (const requirement of drug.requirements) { - if (requirement.stakeholderType === rule.stakeholderType) { - smartLinkCount++; - - // only add the link if the form has not already been processed / received - if (etasu) { - let found = false; - for (const metRequirement of etasu.metRequirements) { - if (metRequirement.requirementName === requirement.name) { - found = true; - if (!metRequirement.completed) { - card.addLink( - createSmartLink(requirement.name, requirement.appContext, contextRequest) - ); - if (patient && patient.resourceType === 'Patient') { - createQuestionnaireSuggestion(card, requirement, patient, contextRequest); - } - unmetRequirementSmartLinkCount++; - } - } - } - if (!found) { - card.addLink( - createSmartLink(requirement.name, requirement.appContext, contextRequest) - ); - if (patient && patient.resourceType === 'Patient') { - createQuestionnaireSuggestion(card, requirement, patient, contextRequest); - } - unmetRequirementSmartLinkCount++; - } - } else { - // add all the required to dispense links if no etasu to check - if (requirement.requiredToDispense) { - card.addLink( - createSmartLink(requirement.name, requirement.appContext, contextRequest) - ); - if (patient && patient.resourceType === 'Patient') { - createQuestionnaireSuggestion(card, requirement, patient, contextRequest); - } - unmetRequirementSmartLinkCount++; - } - } + // count the total requirement for each type + + // find a matching rems case for the patient and this drug to only return needed results + const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; + const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; + const remsCase = await remsCaseCollection.findOne({ + patientFirstName: patientName?.given?.[0], + patientLastName: patientName?.family, + patientDOB: patientBirth, + drugCode: code + }); + const metRequirements = remsCase?.metRequirements; + + const codeRule = (code && codeMap[code]) || []; + + const cards: Card[] = codeRule + .map(rule => { + const card = new Card( + rule.summary || display || 'Rems', + rule.cardDetails || CARD_DETAILS, + source, + 'info' + ); + + // no construction needed + const absoluteLinks = rule.links.filter(e => e.type === 'absolute'); + card.addLinks(absoluteLinks); + + let unmetRequirementSmartLinkCount = 0; + let smartLinkCount = 0; + + const requirements = (drug?.requirements || []).filter( + requirement => requirement.stakeholderType === rule.stakeholderType + ); + + // process the smart links from the medicationCollection + // TODO: smart links should be built with discovered questionnaires, not hard coded ones + for (const requirement of requirements) { + smartLinkCount++; + + // only add the link if the form has not already been processed / received + if (metRequirements) { + const metRequirement = metRequirements.find( + metRequirement => metRequirement.requirementName === requirement.name + ); + const found = Boolean(metRequirement); + if (metRequirement && !metRequirement.completed) { + card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); + if (patient && patient.resourceType === 'Patient') { + createQuestionnaireSuggestion(card, requirement, patient, request); + } + unmetRequirementSmartLinkCount++; + } + if (!found) { + card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); + if (patient && patient.resourceType === 'Patient') { + createQuestionnaireSuggestion(card, requirement, patient, request); + } + unmetRequirementSmartLinkCount++; + } + } else { + // add all the required to dispense links if no etasu to check + if (requirement.requiredToDispense) { + card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); + if (patient && patient.resourceType === 'Patient') { + createQuestionnaireSuggestion(card, requirement, patient, request); } + unmetRequirementSmartLinkCount++; } } + } - // only add the card if there are smart links to needed forms - // allow information only cards to be returned as well - if (unmetRequirementSmartLinkCount > 0 || smartLinkCount === 0) { - cardArray.push(card); - } + // only add the card if there are smart links to needed forms + // allow information only cards to be returned as well + if (unmetRequirementSmartLinkCount > 0 || smartLinkCount === 0) { + return card; } - res.json({ - cards: cardArray - }); - } else { - res.json(buildErrorCard('Unsupported code')); - } - } else { - res.json(buildErrorCard('MedicationRequest does not contain a code')); - } + return []; + }) + .flat(); + + res.json({ cards }); } // handles preliminary card creation. ALL hooks should go through this function. From ff7613ff044c2ec019df522396f0a5d70f89020e Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 13 Jun 2024 14:11:51 -0400 Subject: [PATCH 13/20] Replace variable --- src/hooks/hookResources.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 7b77bda..671fbd6 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -394,7 +394,6 @@ export async function handleCardOrder( patientDOB: patientBirth, drugCode: code }); - const metRequirements = remsCase?.metRequirements; const codeRule = (code && codeMap[code]) || []; @@ -412,7 +411,6 @@ export async function handleCardOrder( card.addLinks(absoluteLinks); let unmetRequirementSmartLinkCount = 0; - let smartLinkCount = 0; const requirements = (drug?.requirements || []).filter( requirement => requirement.stakeholderType === rule.stakeholderType @@ -421,11 +419,9 @@ export async function handleCardOrder( // process the smart links from the medicationCollection // TODO: smart links should be built with discovered questionnaires, not hard coded ones for (const requirement of requirements) { - smartLinkCount++; - // only add the link if the form has not already been processed / received - if (metRequirements) { - const metRequirement = metRequirements.find( + if (remsCase) { + const metRequirement = remsCase.metRequirements.find( metRequirement => metRequirement.requirementName === requirement.name ); const found = Boolean(metRequirement); @@ -455,6 +451,8 @@ export async function handleCardOrder( } } + const smartLinkCount = requirements.length; + // only add the card if there are smart links to needed forms // allow information only cards to be returned as well if (unmetRequirementSmartLinkCount > 0 || smartLinkCount === 0) { From fd01fdd9c3094e7aa4a46ecfd39b7f04c1b2bcbe Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 13 Jun 2024 15:02:55 -0400 Subject: [PATCH 14/20] Simplify if statement --- src/hooks/hookResources.ts | 40 ++++++++++++++------------------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 671fbd6..a6111ae 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -419,35 +419,25 @@ export async function handleCardOrder( // process the smart links from the medicationCollection // TODO: smart links should be built with discovered questionnaires, not hard coded ones for (const requirement of requirements) { - // only add the link if the form has not already been processed / received - if (remsCase) { - const metRequirement = remsCase.metRequirements.find( + const metRequirement = + remsCase && + remsCase.metRequirements.find( metRequirement => metRequirement.requirementName === requirement.name ); - const found = Boolean(metRequirement); - if (metRequirement && !metRequirement.completed) { - card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); - if (patient && patient.resourceType === 'Patient') { - createQuestionnaireSuggestion(card, requirement, patient, request); - } - unmetRequirementSmartLinkCount++; - } - if (!found) { - card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); - if (patient && patient.resourceType === 'Patient') { - createQuestionnaireSuggestion(card, requirement, patient, request); - } - unmetRequirementSmartLinkCount++; - } - } else { + + if ( + // only add the link if the form has not already been processed / received + (metRequirement && !metRequirement.completed) || + // not found + !metRequirement || // add all the required to dispense links if no etasu to check - if (requirement.requiredToDispense) { - card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); - if (patient && patient.resourceType === 'Patient') { - createQuestionnaireSuggestion(card, requirement, patient, request); - } - unmetRequirementSmartLinkCount++; + (!remsCase && requirement.requiredToDispense) + ) { + card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); + if (patient && patient.resourceType === 'Patient') { + createQuestionnaireSuggestion(card, requirement, patient, request); } + unmetRequirementSmartLinkCount++; } } From fb5f94ea996bdf5412356c31d240a85f17933cca Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 13 Jun 2024 16:09:55 -0400 Subject: [PATCH 15/20] Simplify nested if statement into one one-indented if statement --- src/hooks/hookResources.ts | 43 ++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index a6111ae..a36d420 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -383,8 +383,6 @@ export async function handleCardOrder( }) .exec(); - // count the total requirement for each type - // find a matching rems case for the patient and this drug to only return needed results const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; @@ -410,14 +408,15 @@ export async function handleCardOrder( const absoluteLinks = rule.links.filter(e => e.type === 'absolute'); card.addLinks(absoluteLinks); - let unmetRequirementSmartLinkCount = 0; - const requirements = (drug?.requirements || []).filter( requirement => requirement.stakeholderType === rule.stakeholderType ); // process the smart links from the medicationCollection // TODO: smart links should be built with discovered questionnaires, not hard coded ones + const links: Link[] = []; + const suggestions: Suggestion[] = []; + for (const requirement of requirements) { const metRequirement = remsCase && @@ -425,27 +424,31 @@ export async function handleCardOrder( metRequirement => metRequirement.requirementName === requirement.name ); - if ( - // only add the link if the form has not already been processed / received - (metRequirement && !metRequirement.completed) || - // not found - !metRequirement || - // add all the required to dispense links if no etasu to check - (!remsCase && requirement.requiredToDispense) - ) { - card.addLink(createSmartLink(requirement.name, requirement.appContext, request)); + const formNotProcessed = metRequirement && !metRequirement.completed; + const notFound = remsCase && !metRequirement; + const noEtasuToCheckAndRequiredToDispense = !remsCase && requirement.requiredToDispense; + + if (formNotProcessed || notFound || noEtasuToCheckAndRequiredToDispense) { + const smartLink = createSmartLink(requirement.name, requirement.appContext, request); + links.push(smartLink); + if (patient && patient.resourceType === 'Patient') { - createQuestionnaireSuggestion(card, requirement, patient, request); + const suggestion = getQuestionnaireSuggestion(requirement, patient, request); + if (suggestion) { + suggestions.push(suggestion); + } } - unmetRequirementSmartLinkCount++; } } + const unmetRequirementSmartLinkCount = links.length; const smartLinkCount = requirements.length; // only add the card if there are smart links to needed forms // allow information only cards to be returned as well if (unmetRequirementSmartLinkCount > 0 || smartLinkCount === 0) { + card.addLinks(links); + card.addSuggestions(suggestions); return card; } return []; @@ -730,12 +733,11 @@ export const handleCardEncounter = async ( res.json({ cards }); }; -export function createQuestionnaireSuggestion( - card: Card, +export const getQuestionnaireSuggestion = ( requirement: Requirement, patient: Patient, request: MedicationRequest -) { +): Suggestion | undefined => { if (requirement.appContext && requirement.appContext.includes('=')) { const qArr = requirement.appContext.split('='); // break up into parts let qUrl = null; @@ -757,10 +759,11 @@ export function createQuestionnaireSuggestion( label: `Add "Completion of ${requirement.name} Questionnaire" to task list`, actions: [action] }; - card.addSuggestion(suggestion); + return suggestion; } } -} + return undefined; +}; export function createQuestionnaireCompletionTask( requirement: Requirement, patient: Patient, From 6994709d2c6f5bfc80b928f2709fae202170c0ef Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 13 Jun 2024 17:53:50 -0400 Subject: [PATCH 16/20] More refactoring --- src/hooks/hookResources.ts | 154 +++++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 68 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index a36d420..ab9c09c 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -359,13 +359,28 @@ const getErrorCard = ( return null; }; +const getRemsCase = async ( + patient: FhirResource | undefined, + code: string | undefined +): Promise => { + const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; + const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; + const remsCase = await remsCaseCollection.findOne({ + patientFirstName: patientName?.given?.[0], + patientLastName: patientName?.family, + patientDOB: patientBirth, + drugCode: code + }); + return remsCase; +}; + // handles order-sign and order-select currently -export async function handleCardOrder( +export const handleCardOrder = async ( res: TypedResponseBody, hydratedPrefetch: HookPrefetch | undefined, contextRequest: FhirResource | undefined, patient: FhirResource | undefined -) { +): Promise => { const errorCard = getErrorCard(hydratedPrefetch, contextRequest); if (errorCard) { res.json(errorCard); @@ -384,79 +399,82 @@ export async function handleCardOrder( .exec(); // find a matching rems case for the patient and this drug to only return needed results - const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; - const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; - const remsCase = await remsCaseCollection.findOne({ - patientFirstName: patientName?.given?.[0], - patientLastName: patientName?.family, - patientDOB: patientBirth, - drugCode: code - }); + const remsCase = await getRemsCase(patient, code); const codeRule = (code && codeMap[code]) || []; const cards: Card[] = codeRule - .map(rule => { - const card = new Card( - rule.summary || display || 'Rems', - rule.cardDetails || CARD_DETAILS, - source, - 'info' - ); - - // no construction needed - const absoluteLinks = rule.links.filter(e => e.type === 'absolute'); - card.addLinks(absoluteLinks); - - const requirements = (drug?.requirements || []).filter( - requirement => requirement.stakeholderType === rule.stakeholderType - ); - - // process the smart links from the medicationCollection - // TODO: smart links should be built with discovered questionnaires, not hard coded ones - const links: Link[] = []; - const suggestions: Suggestion[] = []; - - for (const requirement of requirements) { - const metRequirement = - remsCase && - remsCase.metRequirements.find( - metRequirement => metRequirement.requirementName === requirement.name - ); - - const formNotProcessed = metRequirement && !metRequirement.completed; - const notFound = remsCase && !metRequirement; - const noEtasuToCheckAndRequiredToDispense = !remsCase && requirement.requiredToDispense; - - if (formNotProcessed || notFound || noEtasuToCheckAndRequiredToDispense) { - const smartLink = createSmartLink(requirement.name, requirement.appContext, request); - links.push(smartLink); - - if (patient && patient.resourceType === 'Patient') { - const suggestion = getQuestionnaireSuggestion(requirement, patient, request); - if (suggestion) { - suggestions.push(suggestion); - } + .map(getCardFromRules(display, drug, remsCase, request, patient)) + .flat(); + + res.json({ cards }); +}; + +const getCardFromRules = + ( + display: string | undefined, + drug: MongooseMedication | null, + remsCase: RemsCase | null, + request: MedicationRequest, + patient: FhirResource | undefined + ) => + (rule: CardRule): Card | never[] => { + const card = new Card( + rule.summary || display || 'Rems', + rule.cardDetails || CARD_DETAILS, + source, + 'info' + ); + + // no construction needed + const absoluteLinks = rule.links.filter(e => e.type === 'absolute'); + card.addLinks(absoluteLinks); + + const requirements = (drug?.requirements || []).filter( + requirement => requirement.stakeholderType === rule.stakeholderType + ); + + // process the smart links from the medicationCollection + // TODO: smart links should be built with discovered questionnaires, not hard coded ones + const links: Link[] = []; + const suggestions: Suggestion[] = []; + + for (const requirement of requirements) { + const metRequirement = + remsCase && + remsCase.metRequirements.find( + metRequirement => metRequirement.requirementName === requirement.name + ); + + const formNotProcessed = metRequirement && !metRequirement.completed; + const notFound = remsCase && !metRequirement; + const noEtasuToCheckAndRequiredToDispense = !remsCase && requirement.requiredToDispense; + + if (formNotProcessed || notFound || noEtasuToCheckAndRequiredToDispense) { + const smartLink = createSmartLink(requirement.name, requirement.appContext, request); + links.push(smartLink); + + if (patient && patient.resourceType === 'Patient') { + const suggestion = getQuestionnaireSuggestion(requirement, patient, request); + if (suggestion) { + suggestions.push(suggestion); } } } + } - const unmetRequirementSmartLinkCount = links.length; - const smartLinkCount = requirements.length; - - // only add the card if there are smart links to needed forms - // allow information only cards to be returned as well - if (unmetRequirementSmartLinkCount > 0 || smartLinkCount === 0) { - card.addLinks(links); - card.addSuggestions(suggestions); - return card; - } - return []; - }) - .flat(); + const unmetRequirementSmartLinkCount = links.length; + const smartLinkCount = requirements.length; - res.json({ cards }); -} + // only add the card if there are smart links to needed forms + // allow information only cards to be returned as well + if (unmetRequirementSmartLinkCount > 0 || smartLinkCount === 0) { + card.addLinks(links); + card.addSuggestions(suggestions); + return card; + } + return []; + }; // handles preliminary card creation. ALL hooks should go through this function. // make sure code here is applicable to all supported hooks. @@ -632,7 +650,7 @@ const containsMatchingMedicationRequest = return false; }; -const getCard = +const getCardFromCases = (entries: BundleEntry[] | undefined) => async ({ drugCode, drugName, metRequirements }: RemsCase): Promise => { // find the drug in the medicationCollection that matches the REMS case to get the smart links @@ -726,7 +744,7 @@ export const handleCardEncounter = async ( }); // loop through all the REMS cases in the list - const promises = remsCaseList.map(getCard(medicationRequestsBundle?.entry)); + const promises = remsCaseList.map(getCardFromCases(medicationRequestsBundle?.entry)); const cards = (await Promise.all(promises)).flat(); From 6199b8b1e98c375f10b5c9180ff20bfc484d120f Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Thu, 13 Jun 2024 20:00:15 -0400 Subject: [PATCH 17/20] Refactor card-producing logic so encounter-start/patient-view and order-sign/order-select callback code reuse SMART-link-grabbing code --- src/hooks/hookResources.ts | 161 +++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 71 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index ab9c09c..eef7a26 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -404,21 +404,23 @@ export const handleCardOrder = async ( const codeRule = (code && codeMap[code]) || []; const cards: Card[] = codeRule - .map(getCardFromRules(display, drug, remsCase, request, patient)) + .map(getCardOrEmptyArrayFromRules(display, drug, remsCase, request, patient)) .flat(); res.json({ cards }); }; -const getCardFromRules = +const getCardOrEmptyArrayFromRules = ( display: string | undefined, drug: MongooseMedication | null, remsCase: RemsCase | null, request: MedicationRequest, - patient: FhirResource | undefined + resource: FhirResource | undefined ) => (rule: CardRule): Card | never[] => { + const patient = resource?.resourceType === 'Patient' ? resource : undefined; + const card = new Card( rule.summary || display || 'Rems', rule.cardDetails || CARD_DETAILS, @@ -430,52 +432,64 @@ const getCardFromRules = const absoluteLinks = rule.links.filter(e => e.type === 'absolute'); card.addLinks(absoluteLinks); - const requirements = (drug?.requirements || []).filter( - requirement => requirement.stakeholderType === rule.stakeholderType - ); + const requirements = + drug?.requirements.filter( + requirement => requirement.stakeholderType === rule.stakeholderType + ) || []; // process the smart links from the medicationCollection // TODO: smart links should be built with discovered questionnaires, not hard coded ones - const links: Link[] = []; - const suggestions: Suggestion[] = []; - - for (const requirement of requirements) { + const predicate = (requirement: Requirement) => { const metRequirement = remsCase && remsCase.metRequirements.find( metRequirement => metRequirement.requirementName === requirement.name ); - const formNotProcessed = metRequirement && !metRequirement.completed; const notFound = remsCase && !metRequirement; const noEtasuToCheckAndRequiredToDispense = !remsCase && requirement.requiredToDispense; - if (formNotProcessed || notFound || noEtasuToCheckAndRequiredToDispense) { - const smartLink = createSmartLink(requirement.name, requirement.appContext, request); - links.push(smartLink); + return formNotProcessed || notFound || noEtasuToCheckAndRequiredToDispense; + }; - if (patient && patient.resourceType === 'Patient') { - const suggestion = getQuestionnaireSuggestion(requirement, patient, request); - if (suggestion) { - suggestions.push(suggestion); - } - } - } - } + const smartLinks: Link[] = getSmartLinks(requirements, request, predicate); + card.addLinks(smartLinks); + + const suggestions: Suggestion[] = getSuggestions(requirements, request, patient, predicate); + card.addSuggestions(suggestions); - const unmetRequirementSmartLinkCount = links.length; + const unmetRequirementSmartLinkCount = smartLinks.length; const smartLinkCount = requirements.length; // only add the card if there are smart links to needed forms // allow information only cards to be returned as well if (unmetRequirementSmartLinkCount > 0 || smartLinkCount === 0) { - card.addLinks(links); - card.addSuggestions(suggestions); return card; } + return []; }; +const getSmartLinks = ( + requirements: Requirement[], + request: MedicationRequest, + predicate: (requirement: Requirement) => boolean +): Link[] => { + return requirements.map(getLinkOrEmptyArray(request, predicate)).flat() || []; +}; + +const getSuggestions = ( + requirements: Requirement[], + request: MedicationRequest, + patient: Patient | undefined, + predicate: (requirement: Requirement) => boolean +): Suggestion[] => { + return ( + (patient && requirements.map(getSuggestionOrEmptyArray(patient, request, predicate)).flat()) || + [] + ); +}; + // handles preliminary card creation. ALL hooks should go through this function. // make sure code here is applicable to all supported hooks. export async function handleCard( @@ -633,12 +647,6 @@ const getSummary = (drugCode: string, drugName: string): string => { return summary; }; -const getAbsoluteLinks = (drugCode: string): Link[] => { - const codeRule = codeMap[drugCode]; - const rule = codeRule.find(rule => rule.stakeholderType === 'patient'); - return rule?.links || []; -}; - const containsMatchingMedicationRequest = (drugCode: string) => (entry: BundleEntry): boolean => { @@ -650,7 +658,7 @@ const containsMatchingMedicationRequest = return false; }; -const getCardFromCases = +const getCardOrEmptyArrayFromCases = (entries: BundleEntry[] | undefined) => async ({ drugCode, drugName, metRequirements }: RemsCase): Promise => { // find the drug in the medicationCollection that matches the REMS case to get the smart links @@ -675,50 +683,61 @@ const getCardFromCases = return []; } + // grab absolute links relevant to the patient + const codeRule = codeMap[drugCode]; + const rule = codeRule.find(rule => rule.stakeholderType === 'patient'); + const absoluteLinks = rule?.links || []; + card.addLinks(absoluteLinks); + + // find all of the matching patient forms + const requirements = + drug?.requirements.filter(requirement => requirement.stakeholderType === 'patient') || []; + // loop through all of the ETASU requirements for this drug - const smartLinks = getSmartLinks(drug, metRequirements, request); - card.addLinks(Array.from(smartLinks)); + const predicate = (requirement: Requirement) => { + // match the requirement to the metRequirement of the REMS case + const metRequirement = metRequirements.find(metRequirement => { + return metRequirement.requirementName === requirement.name; + }); + const formNotProcessed = metRequirement && !metRequirement.completed; + const notFound = !metRequirement; - // grab absolute links relevant to the patient - const absoluteLinks = getAbsoluteLinks(drugCode); - card.addLinks(Array.from(absoluteLinks)); + return formNotProcessed || notFound; + }; + + const smartLinks = getSmartLinks(requirements, request, predicate); + card.addLinks(smartLinks); return card; }; -const getSmartLinks = ( - drug: MongooseMedication | null, - metRequirements: Partial[], - request: MedicationRequest -): Link[] => { - const requirements = drug?.requirements || []; - - const smartLinks = requirements - .map(requirement => { - // find all of the matching patient forms - if (requirement?.stakeholderType === 'patient') { - // match the requirement to the metRequirement of the REMS case - const metRequirement = metRequirements.find(metRequirement => { - return metRequirement.requirementName === requirement.name; - }); - - const link = createSmartLink(requirement.name, requirement.appContext, request); - - if ( - // add the link if the form is still needed to be completed - (!!metRequirement && !metRequirement.completed) || - // if not in the list of metRequirements, add it as well - !metRequirement - ) { - return link; - } - return []; - } - return []; - }) - .flat(); - return smartLinks; -}; +const getLinkOrEmptyArray = + (request: MedicationRequest, predicate: (requirement: Requirement) => boolean) => + (requirement: Requirement): Link | [] => { + const link = createSmartLink(requirement.name, requirement.appContext, request); + + if (predicate(requirement)) { + return link; + } + + return []; + }; + +const getSuggestionOrEmptyArray = + ( + patient: Patient, + request: MedicationRequest, + predicate: (requirement: Requirement) => boolean + ) => + (requirement: Requirement): Suggestion | [] => { + const suggestion = getQuestionnaireSuggestion(requirement, patient, request); + + if (suggestion && predicate(requirement)) { + return suggestion; + } + + return []; + }; // handles patient-view and encounter-start currently export const handleCardEncounter = async ( @@ -744,7 +763,7 @@ export const handleCardEncounter = async ( }); // loop through all the REMS cases in the list - const promises = remsCaseList.map(getCardFromCases(medicationRequestsBundle?.entry)); + const promises = remsCaseList.map(getCardOrEmptyArrayFromCases(medicationRequestsBundle?.entry)); const cards = (await Promise.all(promises)).flat(); From ebea21d9a3b11661fad5a6afb6568d5e9f6b9555 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Fri, 14 Jun 2024 12:27:05 -0400 Subject: [PATCH 18/20] Replace comments with variables --- src/hooks/hookResources.ts | 48 +++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index eef7a26..b22699e 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -359,28 +359,15 @@ const getErrorCard = ( return null; }; -const getRemsCase = async ( - patient: FhirResource | undefined, - code: string | undefined -): Promise => { - const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; - const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; - const remsCase = await remsCaseCollection.findOne({ - patientFirstName: patientName?.given?.[0], - patientLastName: patientName?.family, - patientDOB: patientBirth, - drugCode: code - }); - return remsCase; -}; - // handles order-sign and order-select currently export const handleCardOrder = async ( res: TypedResponseBody, hydratedPrefetch: HookPrefetch | undefined, contextRequest: FhirResource | undefined, - patient: FhirResource | undefined + resource: FhirResource | undefined ): Promise => { + const patient = resource?.resourceType === 'Patient' ? resource : undefined; + const errorCard = getErrorCard(hydratedPrefetch, contextRequest); if (errorCard) { res.json(errorCard); @@ -398,8 +385,15 @@ export const handleCardOrder = async ( }) .exec(); - // find a matching rems case for the patient and this drug to only return needed results - const remsCase = await getRemsCase(patient, code); + // find a matching REMS case for the patient and this drug to only return needed results + const patientName = patient?.name?.[0]; + const patientBirth = patient?.birthDate; + const remsCase = await remsCaseCollection.findOne({ + patientFirstName: patientName?.given?.[0], + patientLastName: patientName?.family, + patientDOB: patientBirth, + drugCode: code + }); const codeRule = (code && codeMap[code]) || []; @@ -416,11 +410,9 @@ const getCardOrEmptyArrayFromRules = drug: MongooseMedication | null, remsCase: RemsCase | null, request: MedicationRequest, - resource: FhirResource | undefined + patient: Patient | undefined ) => (rule: CardRule): Card | never[] => { - const patient = resource?.resourceType === 'Patient' ? resource : undefined; - const card = new Card( rule.summary || display || 'Rems', rule.cardDetails || CARD_DETAILS, @@ -460,10 +452,10 @@ const getCardOrEmptyArrayFromRules = const unmetRequirementSmartLinkCount = smartLinks.length; const smartLinkCount = requirements.length; + const existsSmartLinksToNeededForms = unmetRequirementSmartLinkCount > 0; + const isInformationOnlyCard = smartLinkCount === 0; - // only add the card if there are smart links to needed forms - // allow information only cards to be returned as well - if (unmetRequirementSmartLinkCount > 0 || smartLinkCount === 0) { + if (existsSmartLinksToNeededForms || isInformationOnlyCard) { return card; } @@ -744,8 +736,9 @@ export const handleCardEncounter = async ( res: TypedResponseBody, hookPrefetch: HookPrefetch | undefined, _contextRequest: FhirResource | undefined, - patient: FhirResource | undefined + resource: FhirResource | undefined ): Promise => { + const patient = resource?.resourceType === 'Patient' ? resource : undefined; const medResource = hookPrefetch?.medicationRequests; const medicationRequestsBundle = medResource?.resourceType === 'Bundle' @@ -754,8 +747,8 @@ export const handleCardEncounter = async ( : undefined; // find all matching REMS cases for the patient - const patientName = patient?.resourceType === 'Patient' ? patient?.name?.[0] : undefined; - const patientBirth = patient?.resourceType === 'Patient' ? patient?.birthDate : undefined; + const patientName = patient?.name?.[0]; + const patientBirth = patient?.birthDate; const remsCaseList = await remsCaseCollection.find({ patientFirstName: patientName?.given?.[0], patientLastName: patientName?.family, @@ -801,6 +794,7 @@ export const getQuestionnaireSuggestion = ( } return undefined; }; + export function createQuestionnaireCompletionTask( requirement: Requirement, patient: Patient, From bd75f2daaa28b88573ab30df90a9dcc07c4ce8f7 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Fri, 14 Jun 2024 12:48:31 -0400 Subject: [PATCH 19/20] Add dependency for rems-cds-hooks from origin/main and update pointed commit --- package.json | 1 + src/rems-cds-hooks | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 32d6141..82aee2b 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@types/chai": "^4.3.4", "@types/cors": "^2.8.12", "@types/express": "^4.17.14", + "@types/flat": "^5.0.5", "@types/lodash": "^4.14.188", "@types/mocha": "^10.0.1", "@types/mongodb-memory-server": "2.3.0", diff --git a/src/rems-cds-hooks b/src/rems-cds-hooks index ca6a498..9c11899 160000 --- a/src/rems-cds-hooks +++ b/src/rems-cds-hooks @@ -1 +1 @@ -Subproject commit ca6a498c8086b604b59b2c8037a0a43a65574e7f +Subproject commit 9c11899cace14e71aa9e5da21eb61e05c35b3e60 From 8202e47d542ba3ada652961428c5aa1d07a2d2f3 Mon Sep 17 00:00:00 2001 From: Joyce Quach Date: Fri, 14 Jun 2024 13:07:40 -0400 Subject: [PATCH 20/20] Fix local linting issues --- src/hooks/hookResources.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index b22699e..89aa268 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -20,8 +20,7 @@ import { Requirement, medicationCollection, remsCaseCollection, - Medication as MongooseMedication, - MetRequirements as MongooseMetRequirements + Medication as MongooseMedication } from '../fhir/models'; import axios from 'axios'; @@ -344,7 +343,7 @@ const getErrorCard = ( return buildErrorCard('Context draftOrder does not match prefetch MedicationRequest ID'); } - const medicationCode = getDrugCodeFromMedicationRequest(contextRequest)!; + const medicationCode = getDrugCodeFromMedicationRequest(contextRequest) as Coding; if (!medicationCode?.code) { return buildErrorCard('MedicationRequest does not contain a code'); } @@ -375,7 +374,7 @@ export const handleCardOrder = async ( } // find the drug in the medicationCollection to get the smart links - const coding = !errorCard && getDrugCodeFromMedicationRequest(contextRequest)!; + const coding = !errorCard && (getDrugCodeFromMedicationRequest(contextRequest) as Coding); const { code, system, display } = coding; const request = coding && (contextRequest as MedicationRequest); const drug = await medicationCollection