Skip to content

Commit

Permalink
Updates for bdrs/immigration matching for Botswana
Browse files Browse the repository at this point in the history
  • Loading branch information
pmanko committed Feb 24, 2024
1 parent e8906ab commit a389bab
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 40 deletions.
1 change: 0 additions & 1 deletion .eslintcache

This file was deleted.

2 changes: 1 addition & 1 deletion config/config_docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"loincSystemUrl": "https://api.openconceptlab.org/orgs/Regenstrief/sources/LOINC/",
"omangSystemUrl": "http://moh.bw.org/ext/identifier/omang",
"brdsSystemUrl": "http://moh.bw.org/ext/identifier/bcn",
"immigrationSystemUrl": "http://moh.bw.org/ext/identifier/passportno",
"immigrationSystemUrl": "http://moh.bw.org/ext/identifier/ppn",
"oclUrl": "https://api.openconceptlab.org",
"facilityCodeSystemUrl": "http://moh.bw.org/ext/identifier/facility-code",
"ipmsProviderSystemUrl": "http://moh.bw.org/ext/ipms-provider",
Expand Down
2 changes: 1 addition & 1 deletion debug.docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ services:
container_name: shr
restart: unless-stopped
hostname: shr
image: itechuw/shared-health-record:dev
image: itechuw/shared-health-record:debug
build:
context: ./
args:
Expand Down
3 changes: 3 additions & 0 deletions node-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#! /usr/bin/env bash

docker run -it --rm -v ${PWD}:/usr/src/app -w /usr/src/app node:18-slim /bin/bash
97 changes: 72 additions & 25 deletions src/workflows/botswana/IpmsWorkflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
IPatient,
IReference,
IServiceRequest,
ITask,
TaskStatusKind,
} from '@ahryman40k/ts-fhir-types/lib/R4'
import { saveBundle } from '../../hapi/lab'
Expand Down Expand Up @@ -42,7 +43,7 @@ export async function sendAdtToIpms(labBundle: R4.IBundle): Promise<R4.IBundle>
config.get('bwConfig:mllp:targetAdtPort'),
)

const adtMessage = await Hl7WorkflowsBw.getFhirTranslation(
const adtMessage = await Hl7WorkflowsBw.getFhirTranslationWithRetry(
labBundle,
config.get('bwConfig:toIpmsAdtTemplate'),
)
Expand All @@ -52,7 +53,7 @@ export async function sendAdtToIpms(labBundle: R4.IBundle): Promise<R4.IBundle>
const adtResult: string = <string>await sender.send(adtMessage)

if (adtResult.includes && adtResult.includes('AA')) {
labBundle = setTaskStatus(labBundle, R4.TaskStatusKind._accepted)
labBundle = setTaskStatus(labBundle, R4.TaskStatusKind._received)
}
} else {
logger.info('Order not ready for IPMS.')
Expand Down Expand Up @@ -94,7 +95,7 @@ export async function sendOrmToIpms(bundles: any): Promise<R4.IBundle> {

const fetchedBundle = <
R4.IBundle // TODO: Retry logic
>await got.get(`${config.get('fhirServer:baseURL')}/ServiceRequest`, options).json()
>await got.get(`${config.get('fhirServer:baseURL')}/ServiceRequest`, options).json()

if (fetchedBundle && fetchedBundle.entry && srBundle.entry) {
// Add child ServiceRequests if any exist
Expand Down Expand Up @@ -125,7 +126,7 @@ export async function sendOrmToIpms(bundles: any): Promise<R4.IBundle> {
const outBundle = { ...sendBundle }
outBundle.entry.push(sr)

const ormMessage = await Hl7WorkflowsBw.getFhirTranslation(
const ormMessage = await Hl7WorkflowsBw.getFhirTranslationWithRetry(
outBundle,
config.get('bwConfig:toIpmsOrmTemplate'),
)
Expand All @@ -142,7 +143,7 @@ export async function sendOrmToIpms(bundles: any): Promise<R4.IBundle> {
if (ormMessage && ormMessage != '') {
const result: any = await sender.send(ormMessage)
if (result.includes('AA')) {
labBundle = setTaskStatus(labBundle, R4.TaskStatusKind._inProgress)
labBundle = setTaskStatus(labBundle, R4.TaskStatusKind._accepted)
}
logger.info(`*result:\n${result}\n`)
}
Expand All @@ -156,6 +157,9 @@ export async function sendOrmToIpms(bundles: any): Promise<R4.IBundle> {

/**
* Handles ADT (Admission, Discharge, Transfer) messages received from IPMS (Integrated Patient Management System).
*
* This method needs to be able to match the patient coming back to the patient going in.
*
* @param registrationBundle - The registration bundle containing the patient information.
* @returns A Promise that resolves to the registration bundle.
*/
Expand All @@ -176,55 +180,98 @@ export async function handleAdtFromIpms(adtMessage: string): Promise<any> {
}

// Get patient from registration Bundle
let patient: R4.IPatient, omang: string
let patient: R4.IPatient, omang: string, ppn: string, bcn: string, identifierParam: string

const patEntry = registrationBundle.entry!.find(entry => {
return entry.resource && entry.resource.resourceType == 'Patient'
})

if (patEntry && patEntry.resource) {
patient = <R4.IPatient>patEntry.resource

// Find patient identifiers, if they exist
const omangEntry = patient.identifier?.find(
i => i.system && i.system == config.get('bwConfig:omangSystemUrl'),
)

const ppnEntry = patient.identifier?.find(
i => i.system && i.system == config.get('bwConfig:immigrationSystemUrl'),
)

const bcnEntry = patient.identifier?.find(
i => i.system && i.system == config.get('bwConfig:bdrsSystemUrl'),
)

if (omangEntry && omangEntry.value) {
omang = omangEntry.value
identifierParam = `${config.get('bwConfig:omangSystemUrl')}|${omang}`
} else if (bcnEntry && bcnEntry.value) {
bcn = bcnEntry.value
identifierParam = `${config.get('bwConfig:bdrsSystemUrl')}|${bcn}`
} else if (ppnEntry && ppnEntry.value) {
ppn = ppnEntry.value
identifierParam = `${config.get('bwConfig:immigrationSystemUrl')}|${ppn}`
} else {
logger.error(
'Missing Omang - currently, only matching on Omang supported, but patient does not have an Omang number.',
)
let errorMessage = 'Patient missing a required identifier - matching supported only on Omang, birth certificate number, or passport number.'

logger.error(errorMessage)

throw new IpmsWorkflowError(
`Missing Omang - currently, only matching on Omang supported, but patient does not have an Omang number.`,
errorMessage
)
}

// Find all patients with this Omang.
// Find all patients with these identifiers and grab the related Tasks
options.searchParams = {
identifier: `${config.get('bwConfig:omangSystemUrl')}|${omang}`,
identifier: `${identifierParam}`,
_revinclude: 'Task:patient',
}

let patientTasks: R4.IBundle
let potentialPatientTasks: R4.IBundle
try {
patientTasks = await got.get(`${config.get('fhirServer:baseURL')}/Patient`, options).json()
potentialPatientTasks = await got.get(`${config.get('fhirServer:baseURL')}/Patient`, options).json()
} catch (e) {
patientTasks = { resourceType: 'Bundle' }
potentialPatientTasks = { resourceType: 'Bundle' }
logger.error(e)
}

if (patientTasks && patientTasks.entry) {
// Get all Tasks with `requested` status
for (const e of patientTasks.entry!) {
if (
e.resource &&
e.resource.resourceType == 'Task' &&
(e.resource.status == TaskStatusKind._accepted || config.get('bwConfig:devTaskStatus'))
) {
if (potentialPatientTasks && potentialPatientTasks.entry) {
// Get all Tasks with `recieved` status, which indicates the patient ADT has been sent to IPMS

// Filter and Sort all resources in entry to have tasks by decending order or creation
const patientTasks = potentialPatientTasks.entry.filter(
e => e.resource && e.resource.resourceType == 'Task' && (e.resource.status == TaskStatusKind._received),
).sort((a, b) => {
if (a.resource && b.resource) {
let at = <ITask>a.resource
let bt = <ITask>b.resource

return (
new Date(bt.authoredOn || 0).getTime() -
new Date(at.authoredOn || 0).getTime()
)
}
return 0
})

// TODO: Account for multiple task results!

// For now, if multiple tasks exist, grab the most recent one and log a warning
if (patientTasks.length > 1) {
logger.warn(
`More than one task found for patient ${patient.id} with identifier ${identifierParam}! Processing most recent.`,
)
}

if (patientTasks.length > 0) {
const targetTask = patientTasks[0].resource

if (targetTask) {

// Grab bundle for task:
options.searchParams = {
_include: '*',
_id: e.resource.id,
_id: targetTask.id,
}

const taskBundle: IBundle = await got
Expand All @@ -235,7 +282,7 @@ export async function handleAdtFromIpms(adtMessage: string): Promise<any> {
}
return { patient: undefined, taskBundle: undefined }
} else {
logger.error('Could not find patient tasks!')
logger.error('Could not find any patient tasks for patient with identifier ' + identifierParam + '!')
return { patient: undefined, taskBundle: undefined }
}
}
Expand Down
Loading

0 comments on commit a389bab

Please sign in to comment.