From 92de7755ff5205dc4830c36a661b955b107fe18a Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Fri, 12 Sep 2025 13:01:24 -0400 Subject: [PATCH 1/7] add pharmacy details to cds hooks details --- src/hooks/hookResources.ts | 82 ++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index deaccad..b80246c 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -4,6 +4,7 @@ import { FhirResource, Task, Patient, + Organization, Bundle, Medication, BundleEntry @@ -16,7 +17,8 @@ import { Requirement, medicationCollection, remsCaseCollection, - Medication as MongooseMedication + Medication as MongooseMedication, + metRequirementsCollection } from '../fhir/models'; import axios from 'axios'; @@ -368,6 +370,8 @@ export const handleCardOrder = async ( ): Promise => { const patient = resource?.resourceType === 'Patient' ? resource : undefined; + const pharmacy = hydratedPrefetch?.pharmacy as Organization; + const errorCard = getErrorCard(hydratedPrefetch, contextRequest); if (errorCard) { res.json(errorCard); @@ -397,9 +401,11 @@ export const handleCardOrder = async ( const codeRule = (code && codeMap[code]) || []; - const cards: Card[] = codeRule - .map(getCardOrEmptyArrayFromRules(display, drug, remsCase, request, patient)) - .flat(); + const cardPromises = codeRule.map( + getCardOrEmptyArrayFromRules(display, drug, remsCase, request, patient, pharmacy) + ); + + const cards: Card[] = (await Promise.all(cardPromises)).flat(); res.json({ cards }); }; @@ -410,12 +416,26 @@ const getCardOrEmptyArrayFromRules = drug: MongooseMedication | null, remsCase: RemsCase | null, request: MedicationRequest, - patient: Patient | undefined + patient: Patient | undefined, + pharmacy: Organization | undefined ) => - (rule: CardRule): Card | never[] => { + async (rule: CardRule): Promise => { + let pharmacyInfo = ''; + if (pharmacy && pharmacy) { + const isCertified = await checkPharmacyCertification(pharmacy, drug?.code); // AWAIT HERE + + const pharmacyName = pharmacy.name || pharmacy.alias?.[0] || 'Selected pharmacy'; + + pharmacyInfo = `**Pharmacy Status:** ${pharmacyName} is ${ + isCertified ? 'certified' : 'not yet certified' + } for ${display} REMS dispensing. This medication ${ + isCertified ? 'can' : 'cannot yet' + } be dispensed at this location.\n\n`; + } + const card = new Card( rule.summary || display || 'Rems', - rule.cardDetails || CARD_DETAILS, + pharmacyInfo + (rule.cardDetails || CARD_DETAILS), source, 'info' ); @@ -462,6 +482,53 @@ const getCardOrEmptyArrayFromRules = return []; }; +const checkPharmacyCertification = async ( + pharmacy: Organization | undefined, + drugCode: string | undefined +) => { + if (!pharmacy?.id || !drugCode) { + return false; + } + + const drug = await medicationCollection + .findOne({ + code: drugCode, + codeSystem: 'http://www.nlm.nih.gov/research/umls/rxnorm' + }) + .exec(); + + if (!drug) { + return false; + } + + const requiredPharmacistRequirements = drug.requirements.filter( + requirement => requirement.stakeholderType === 'pharmacist' && requirement.requiredToDispense + ); + + if (requiredPharmacistRequirements.length === 0) { + return true; + } + + const pharmacyId = `Organization/${pharmacy.id}`; + + for (const requirement of requiredPharmacistRequirements) { + const metRequirement = await metRequirementsCollection + .findOne({ + stakeholderId: pharmacyId, + requirementName: requirement.name, + drugName: drug.name, + completed: true + }) + .exec(); + + if (!metRequirement) { + return false; + } + } + + return true; +}; + const getSmartLinks = ( requirements: Requirement[], request: MedicationRequest, @@ -738,6 +805,7 @@ export const handleCardEncounter = async ( _contextRequest: FhirResource | undefined, resource: FhirResource | undefined ): Promise => { + const patient = resource?.resourceType === 'Patient' ? resource : undefined; const medResource = hookPrefetch?.medicationRequests; const medicationRequestsBundle = From 1f37332b91e50015aa765454a8cab908ab9e5b44 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Fri, 12 Sep 2025 13:10:00 -0400 Subject: [PATCH 2/7] run lint --- src/hooks/hookResources.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index b80246c..f7c4c81 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -805,7 +805,6 @@ export const handleCardEncounter = async ( _contextRequest: FhirResource | undefined, resource: FhirResource | undefined ): Promise => { - const patient = resource?.resourceType === 'Patient' ? resource : undefined; const medResource = hookPrefetch?.medicationRequests; const medicationRequestsBundle = From 2594089c88569d402d3432d0fe7fdbd7c9eb98f2 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 10:20:27 -0400 Subject: [PATCH 3/7] update pharmacy resource to healthcare service --- src/fhir/utilities.ts | 8 ++++---- src/hooks/hookResources.ts | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/fhir/utilities.ts b/src/fhir/utilities.ts index de0cde9..72e66b3 100644 --- a/src/fhir/utilities.ts +++ b/src/fhir/utilities.ts @@ -311,7 +311,7 @@ export class FhirUtilities { const medicationRequirements = [ { - stakeholderId: 'Organization/pharm0111', + stakeholderId: 'HealthcareService/pharm0111', completed: true, requirementName: 'Pharmacist Enrollment', drugName: 'Turalio', @@ -319,7 +319,7 @@ export class FhirUtilities { case_numbers: [] }, { - stakeholderId: 'Organization/pharm0111', + stakeholderId: 'HealthcareService/pharm0111', completed: true, requirementName: 'Pharmacist Enrollment', drugName: 'TIRF', @@ -327,7 +327,7 @@ export class FhirUtilities { case_numbers: [] }, { - stakeholderId: 'Organization/pharm0111', + stakeholderId: 'HealthcareService/pharm0111', completed: true, requirementName: 'Pharmacist Knowledge Assessment', drugName: 'TIRF', @@ -335,7 +335,7 @@ export class FhirUtilities { case_numbers: [] }, { - stakeholderId: 'Organization/pharm0111', + stakeholderId: 'HealthcareService/pharm0111', completed: true, requirementName: 'Pharmacist Enrollment', drugName: 'Isotretinoin', diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index f7c4c81..5825084 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -4,10 +4,10 @@ import { FhirResource, Task, Patient, - Organization, Bundle, Medication, - BundleEntry + BundleEntry, + HealthcareService } from 'fhir/r4'; import Card, { Link, Suggestion, Action } from '../cards/Card'; import { HookPrefetch, TypedRequestBody } from '../rems-cds-hooks/resources/HookTypes'; @@ -370,7 +370,11 @@ export const handleCardOrder = async ( ): Promise => { const patient = resource?.resourceType === 'Patient' ? resource : undefined; - const pharmacy = hydratedPrefetch?.pharmacy as Organization; + console.log('hydratedPrefetch: ' + JSON.stringify(hydratedPrefetch)); + + const pharmacy = hydratedPrefetch?.pharmacy as HealthcareService; + + console.log(' Pharmacy: ' + pharmacy); const errorCard = getErrorCard(hydratedPrefetch, contextRequest); if (errorCard) { @@ -417,11 +421,12 @@ const getCardOrEmptyArrayFromRules = remsCase: RemsCase | null, request: MedicationRequest, patient: Patient | undefined, - pharmacy: Organization | undefined + pharmacy: HealthcareService | undefined ) => async (rule: CardRule): Promise => { let pharmacyInfo = ''; - if (pharmacy && pharmacy) { + console.log('Checking pharmacy certification for ' + pharmacy) + if (pharmacy) { const isCertified = await checkPharmacyCertification(pharmacy, drug?.code); // AWAIT HERE const pharmacyName = pharmacy.name || pharmacy.alias?.[0] || 'Selected pharmacy'; @@ -483,7 +488,7 @@ const getCardOrEmptyArrayFromRules = }; const checkPharmacyCertification = async ( - pharmacy: Organization | undefined, + pharmacy: HealthcareService | undefined, drugCode: string | undefined ) => { if (!pharmacy?.id || !drugCode) { @@ -509,7 +514,7 @@ const checkPharmacyCertification = async ( return true; } - const pharmacyId = `Organization/${pharmacy.id}`; + const pharmacyId = `HealthcareService/${pharmacy.id}`; for (const requirement of requiredPharmacistRequirements) { const metRequirement = await metRequirementsCollection From 9ab7ac264d3f86e73df6a742526536ead457455e Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 10:25:41 -0400 Subject: [PATCH 4/7] run prettier/lint --- src/hooks/hookResources.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index 5825084..fcd9fd1 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -425,11 +425,11 @@ const getCardOrEmptyArrayFromRules = ) => async (rule: CardRule): Promise => { let pharmacyInfo = ''; - console.log('Checking pharmacy certification for ' + pharmacy) + console.log('Checking pharmacy certification for ' + pharmacy); if (pharmacy) { const isCertified = await checkPharmacyCertification(pharmacy, drug?.code); // AWAIT HERE - const pharmacyName = pharmacy.name || pharmacy.alias?.[0] || 'Selected pharmacy'; + const pharmacyName = pharmacy.name || 'Selected pharmacy'; pharmacyInfo = `**Pharmacy Status:** ${pharmacyName} is ${ isCertified ? 'certified' : 'not yet certified' From ad29b4e14b140b138c0da24f05916ee1961af487 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 13:53:02 -0400 Subject: [PATCH 5/7] put pharmacy info in its own card --- src/hooks/hookResources.ts | 67 +++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index fcd9fd1..c309333 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -406,12 +406,56 @@ export const handleCardOrder = async ( const codeRule = (code && codeMap[code]) || []; const cardPromises = codeRule.map( - getCardOrEmptyArrayFromRules(display, drug, remsCase, request, patient, pharmacy) + getCardOrEmptyArrayFromRules(display, drug, remsCase, request, patient) ); - const cards: Card[] = (await Promise.all(cardPromises)).flat(); + const remsCards: Card[] = (await Promise.all(cardPromises)).flat(); + + // Create pharmacy status card once (if pharmacy exists) + const allCards: Card[] = []; + if (pharmacy) { + const pharmacyStatusCard = await createPharmacyStatusCard(pharmacy, drug, display); + if (pharmacyStatusCard) { + allCards.push(pharmacyStatusCard); + } + } + + // Add all REMS cards after the pharmacy card + allCards.push(...remsCards); - res.json({ cards }); + res.json({ cards: allCards }); +}; + + +const createPharmacyStatusCard = async ( + pharmacy: HealthcareService, + drug: MongooseMedication | null, + display: string | undefined +): Promise => { + if (!pharmacy) { + return null; + } + + const isCertified = await checkPharmacyCertification(pharmacy, drug?.code); + const pharmacyName = pharmacy.name || 'Selected pharmacy'; + const locationInfo = pharmacy.location?.[0]?.display; + const fullPharmacyName = `${pharmacyName} (${locationInfo})`; + + const statusText = `${fullPharmacyName} is ${ + isCertified ? 'certified' : 'not yet certified' + } for ${display || 'this medication'} REMS dispensing. This medication ${ + isCertified ? 'can' : 'cannot yet' + } be dispensed at this location.`; + + const pharmacyStatusCard = new Card( + 'Pharmacy Certification Status', + statusText, + source, + isCertified ? 'info' : 'warning' + ); + + // No links or suggestions for this card - it's informational only + return pharmacyStatusCard; }; const getCardOrEmptyArrayFromRules = @@ -421,26 +465,11 @@ const getCardOrEmptyArrayFromRules = remsCase: RemsCase | null, request: MedicationRequest, patient: Patient | undefined, - pharmacy: HealthcareService | undefined ) => async (rule: CardRule): Promise => { - let pharmacyInfo = ''; - console.log('Checking pharmacy certification for ' + pharmacy); - if (pharmacy) { - const isCertified = await checkPharmacyCertification(pharmacy, drug?.code); // AWAIT HERE - - const pharmacyName = pharmacy.name || 'Selected pharmacy'; - - pharmacyInfo = `**Pharmacy Status:** ${pharmacyName} is ${ - isCertified ? 'certified' : 'not yet certified' - } for ${display} REMS dispensing. This medication ${ - isCertified ? 'can' : 'cannot yet' - } be dispensed at this location.\n\n`; - } - const card = new Card( rule.summary || display || 'Rems', - pharmacyInfo + (rule.cardDetails || CARD_DETAILS), + rule.cardDetails || CARD_DETAILS, source, 'info' ); From 613c24df1e101e584e4c07c3b7f31c4b825811fe Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 13:57:59 -0400 Subject: [PATCH 6/7] lint --- 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 c309333..e4ea5db 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -410,7 +410,7 @@ export const handleCardOrder = async ( ); const remsCards: Card[] = (await Promise.all(cardPromises)).flat(); - + // Create pharmacy status card once (if pharmacy exists) const allCards: Card[] = []; if (pharmacy) { @@ -419,14 +419,13 @@ export const handleCardOrder = async ( allCards.push(pharmacyStatusCard); } } - + // Add all REMS cards after the pharmacy card allCards.push(...remsCards); res.json({ cards: allCards }); }; - const createPharmacyStatusCard = async ( pharmacy: HealthcareService, drug: MongooseMedication | null, @@ -464,7 +463,7 @@ const getCardOrEmptyArrayFromRules = drug: MongooseMedication | null, remsCase: RemsCase | null, request: MedicationRequest, - patient: Patient | undefined, + patient: Patient | undefined ) => async (rule: CardRule): Promise => { const card = new Card( From a8a32a9bd10343578496448689d4c2de5d9c9047 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 16:10:56 -0400 Subject: [PATCH 7/7] add markdown bolding to pharmacy status --- src/hooks/hookResources.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/hookResources.ts b/src/hooks/hookResources.ts index e4ea5db..7646a44 100644 --- a/src/hooks/hookResources.ts +++ b/src/hooks/hookResources.ts @@ -440,11 +440,11 @@ const createPharmacyStatusCard = async ( const locationInfo = pharmacy.location?.[0]?.display; const fullPharmacyName = `${pharmacyName} (${locationInfo})`; - const statusText = `${fullPharmacyName} is ${ + const statusText = `${fullPharmacyName} **is ${ isCertified ? 'certified' : 'not yet certified' - } for ${display || 'this medication'} REMS dispensing. This medication ${ + }** for ${display || 'this medication'} REMS dispensing. This medication **${ isCertified ? 'can' : 'cannot yet' - } be dispensed at this location.`; + }** be dispensed at this location.`; const pharmacyStatusCard = new Card( 'Pharmacy Certification Status',