Skip to content

Commit

Permalink
Updated BW workflows for OpenMRS lab interoperability (#79)
Browse files Browse the repository at this point in the history
Updates post-rebase

IPMS Workflow updates

Updating lab workflows with location mappings

fix

Bundle format fixes

Workflow updates

Workflow updates

fixes

Location workflow fixes

Retries and logging of MLLP sender'

Pilot updates

Test fix

Removed skipped tests

svu

update
  • Loading branch information
pmanko authored Oct 14, 2023
1 parent b0b978d commit 3667a80
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .eslintcache
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"/u01/code/shared-health-record/src/lib/hl7MllpSender.ts":"1","/u01/code/shared-health-record/src/workflows/labWorkflowsBw.ts":"2","/u01/code/shared-health-record/src/workflows/__tests__/labWorkflowsBw.ts":"3"},{"size":1599,"mtime":1691536925750,"results":"4","hashOfConfig":"5"},{"size":30252,"mtime":1691536926174},{"size":3190,"mtime":1691537800246},{"filePath":"6","messages":"7","suppressedMessages":"8","errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"h0u5wi","/u01/code/shared-health-record/src/lib/hl7MllpSender.ts",["9","10","11"],[],{"ruleId":"12","severity":1,"message":"13","line":19,"column":40,"nodeType":"14","messageId":"15","endLine":19,"endColumn":43,"suggestions":"16"},{"ruleId":"12","severity":1,"message":"13","line":25,"column":75,"nodeType":"14","messageId":"15","endLine":25,"endColumn":78,"suggestions":"17"},{"ruleId":"12","severity":1,"message":"13","line":25,"column":89,"nodeType":"14","messageId":"15","endLine":25,"endColumn":92,"suggestions":"18"},"@typescript-eslint/no-explicit-any","Unexpected any. Specify a different type.","TSAnyKeyword","unexpectedAny",["19","20"],["21","22"],["23","24"],{"messageId":"25","fix":"26","desc":"27"},{"messageId":"28","fix":"29","desc":"30"},{"messageId":"25","fix":"31","desc":"27"},{"messageId":"28","fix":"32","desc":"30"},{"messageId":"25","fix":"33","desc":"27"},{"messageId":"28","fix":"34","desc":"30"},"suggestUnknown",{"range":"35","text":"36"},"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.","suggestNever",{"range":"35","text":"37"},"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of.",{"range":"38","text":"36"},{"range":"38","text":"37"},{"range":"39","text":"36"},{"range":"39","text":"37"},[453,456],"unknown","never",[752,755],[766,769]]
[{"/u01/code/shared-health-record/src/lib/hl7MllpSender.ts":"1","/u01/code/shared-health-record/src/workflows/labWorkflowsBw.ts":"2","/u01/code/shared-health-record/src/workflows/__tests__/labWorkflowsBw.ts":"3"},{"size":1599,"mtime":1691536925750,"results":"4","hashOfConfig":"5"},{"size":30252,"mtime":1691536926174},{"size":3190,"mtime":1691537800246},{"filePath":"6","messages":"7","suppressedMessages":"8","errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"h0u5wi","/u01/code/shared-health-record/src/lib/hl7MllpSender.ts",["9","10","11"],[],{"ruleId":"12","severity":1,"message":"13","line":19,"column":40,"nodeType":"14","messageId":"15","endLine":19,"endColumn":43,"suggestions":"16"},{"ruleId":"12","severity":1,"message":"13","line":25,"column":75,"nodeType":"14","messageId":"15","endLine":25,"endColumn":78,"suggestions":"17"},{"ruleId":"12","severity":1,"message":"13","line":25,"column":89,"nodeType":"14","messageId":"15","endLine":25,"endColumn":92,"suggestions":"18"},"@typescript-eslint/no-explicit-any","Unexpected any. Specify a different type.","TSAnyKeyword","unexpectedAny",["19","20"],["21","22"],["23","24"],{"messageId":"25","fix":"26","desc":"27"},{"messageId":"28","fix":"29","desc":"30"},{"messageId":"25","fix":"31","desc":"27"},{"messageId":"28","fix":"32","desc":"30"},{"messageId":"25","fix":"33","desc":"27"},{"messageId":"28","fix":"34","desc":"30"},"suggestUnknown",{"range":"35","text":"36"},"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.","suggestNever",{"range":"35","text":"37"},"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of.",{"range":"38","text":"36"},{"range":"38","text":"37"},{"range":"39","text":"36"},{"range":"39","text":"37"},[453,456],"unknown","never",[752,755],[766,769]]
5 changes: 3 additions & 2 deletions config/config_docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
"mediator": {
"api": {
"username": "[email protected]",
"password": "HLh6eX4f?#tc4R@F",
"password": "openhim",
"apiURL": "https://openhim-core:8080",
"trustSelfSigned": true,
"urn": "urn:mediator:shared-health-record"
},
"client": {
"username": "shr-client",
"password": "k?f?NPJ9NRG3p3A9"
"password": "shr-client"
}
},
"fhirServer": {
Expand All @@ -40,6 +40,7 @@
"ipmsPatientTypeSystemUrl": "http://moh.bw.org/ext/ipms-patient-type",
"ipmsPatientStatusSystemUrl": "http://moh.bw.org/ext/ipms-patient-status",
"ipmsXLocationSystemUrl": "http://moh.bw.org/ext/ipms-xlocation",
"ipmsOrderTypeSystemUrl": "http://moh.bw.org/ext/ipms-order-type",
"requestTimeout": 60000,
"toIpmsAdtTemplate": "ADT_A04_TO_IPMS.hbs",
"fromIpmsAdtTemplate": "ADT_A04_FROM_IPMS.hbs",
Expand Down
Binary file modified config/ipms_facility_mappings.xlsx
Binary file not shown.
Binary file added config/ipms_facility_mappings_v0.xlsx
Binary file not shown.
2 changes: 1 addition & 1 deletion debug.docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ services:
restart: unless-stopped
hostname: shr
restart: unless-stopped
image: ghcr.io/i-tech-uw/shared-health-record:local
image: ghcr.io/i-tech-uw/shared-health-record:debug-2
build:
context: ./
args:
Expand Down
16 changes: 7 additions & 9 deletions src/lib/locationMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ type FacilityMapping = {
provider: string
patientType: string
patientStatus: string
futurePatientStatus: string
xLocation: string
}

Expand All @@ -22,23 +21,22 @@ async function getFacilityMappings() {

const workbook = new Excel.Workbook()
const content = await workbook.xlsx.readFile(locationMapFile)
const worksheet = content.getWorksheet('HIE')
const worksheet = content.getWorksheet('LIVE')

const rowStartIndex = 2
const rowEndIndex = 11
const rowEndIndex = 90

const rows = worksheet.getRows(rowStartIndex, rowEndIndex) ?? []

const mappings = rows.map((row: Excel.Row): FacilityMapping => {
return {
index: parseInt(getCellValue(row, 1)),
orderingFacility: getCellValue(row, 2),
receivingFacility: getCellValue(row, 3),
provider: getCellValue(row, 5),
patientType: getCellValue(row, 6),
orderingFacility: getCellValue(row, 4),
receivingFacility: getCellValue(row, 2),
provider: getCellValue(row, 6),
patientType: getCellValue(row, 8),
patientStatus: getCellValue(row, 7),
futurePatientStatus: getCellValue(row, 8),
xLocation: getCellValue(row, 9),
xLocation: getCellValue(row, 3),
}
})

Expand Down
2 changes: 1 addition & 1 deletion src/server/__tests__/mllpAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BundleTypeKind, IBundle } from '@ahryman40k/ts-fhir-types/lib/R4'
import { promises as fs } from 'fs'
import fs from 'fs/promises'
import path from 'path'
import Hl7Workflows from '../../workflows/hl7WorkflowsBw'
import MllpAdapter from '../mllpAdapter'
Expand Down
56 changes: 49 additions & 7 deletions src/workflows/labWorkflowsBw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ export class LabWorkflowsBw extends LabWorkflows {
default:
break
}
await new Promise(resolve => setTimeout(resolve, 300))

return res
} catch (e) {
logger.error(e)
Expand All @@ -150,6 +152,7 @@ export class LabWorkflowsBw extends LabWorkflows {
e.resource.code.coding &&
e.resource.code.coding.length > 0
) {
logger.info`Translating ServiceRequest Codings`
e.resource = await this.translateCoding(e.resource)
}
}
Expand Down Expand Up @@ -317,10 +320,14 @@ export class LabWorkflowsBw extends LabWorkflows {
pimsCoding = this.getCoding(sr, config.get('bwConfig:pimsSystemUrl'))
cielCoding = this.getCoding(sr, config.get('bwConfig:cielSystemUrl'))

logger.info(`PIMS Coding: ${JSON.stringify(pimsCoding)}`)
logger.info(`CIEL Coding: ${JSON.stringify(cielCoding)}`)

if (pimsCoding && pimsCoding.code) {
// Translate from PIMS to CIEL and IPMS
ipmsCoding = await this.getIpmsCode(
`/orgs/I-TECH-UW/sources/IPMSLAB/mappings?toConcept=${pimsCoding.code}&toConceptSource=PIMSLAB`,
pimsCoding.code,
)

if (ipmsCoding && ipmsCoding.code) {
Expand All @@ -340,16 +347,25 @@ export class LabWorkflowsBw extends LabWorkflows {
// Translate from CIEL to IPMS
ipmsCoding = await this.getIpmsCode(
`/orgs/I-TECH-UW/sources/IPMSLAB/mappings?toConcept=${cielCoding.code}&toConceptSource=CIEL`,
cielCoding.code,
)
}

// Add IPMS Coding
if (ipmsCoding && ipmsCoding.code) {
sr.code.coding.push({
const ipmsOrderTypeExt = {
url: config.get('bwConfig:ipmsOrderTypeSystemUrl'),
valueString: ipmsCoding.hl7Flag,
}

const srCoding = {
system: config.get('bwConfig:ipmsSystemUrl'),
code: ipmsCoding.mnemonic,
display: ipmsCoding.display,
})
extension: [ipmsOrderTypeExt],
}

sr.code.coding.push(srCoding)
}

// Get LOINC Coding
Expand Down Expand Up @@ -944,17 +960,23 @@ export class LabWorkflowsBw extends LabWorkflows {
}
}

private static async getIpmsCode(q: string) {
private static async getIpmsCode(q: string, c = '') {
try {
const ipmsMappings = await this.getOclMapping(q)

//logger.info(`IPMS Mappings: ${JSON.stringify(ipmsMappings)}`)

// Prioritize "Broader Than Mappings"
//TODO: Figure out if this is proper way to handle panels / broad to narrow
let mappingIndex = ipmsMappings.findIndex((x: any) => x.map_type == 'BROADER-THAN')
let mappingIndex = ipmsMappings.findIndex(
(x: any) => x.map_type == 'BROADER-THAN' && x.to_concept_code == c,
)

// Fall back to "SAME AS"
if (mappingIndex < 0) {
mappingIndex = ipmsMappings.findIndex((x: any) => x.map_type == 'SAME-AS')
mappingIndex = ipmsMappings.findIndex(
(x: any) => x.map_type == 'SAME-AS' && x.to_concept_code == c,
)
}

if (mappingIndex >= 0) {
Expand All @@ -963,12 +985,17 @@ export class LabWorkflowsBw extends LabWorkflows {
const ipmsCodingInfo: any = await this.getOclMapping(
`/orgs/I-TECH-UW/sources/IPMSLAB/concepts/${ipmsCode}`,
)
let ipmsMnemonic
// logger.info(`IPMS Coding Info: ${JSON.stringify(ipmsCodingInfo)}`)
let ipmsMnemonic, hl7Flag
if (ipmsCodingInfo) {
ipmsMnemonic = ipmsCodingInfo.names.find((x: any) => x.name_type == 'Short').name
hl7Flag =
ipmsCodingInfo.extras && ipmsCodingInfo.extras.IPMS_HL7_ORM_TYPE
? ipmsCodingInfo.extras.IPMS_HL7_ORM_TYPE
: 'LAB'
}

return { code: ipmsCode, display: ipmsDisplay, mnemonic: ipmsMnemonic }
return { code: ipmsCode, display: ipmsDisplay, mnemonic: ipmsMnemonic, hl7Flag: hl7Flag }
} else {
return null
}
Expand All @@ -982,6 +1009,8 @@ export class LabWorkflowsBw extends LabWorkflows {
try {
const codeMapping = await this.getOclMapping(q)

//logger.info(`Code Mapping: ${JSON.stringify(codeMapping)}`)

if (codeMapping && codeMapping.length > 0) {
return {
code: codeMapping[0].to_concept_code,
Expand All @@ -999,7 +1028,20 @@ export class LabWorkflowsBw extends LabWorkflows {
private static async getOclMapping(queryString: string): Promise<any[]> {
const options = { timeout: config.get('bwConfig:requestTimeout') | 1000 }

logger.info(`${config.get('bwConfig:oclUrl')}${queryString}`)

return got.get(`${config.get('bwConfig:oclUrl')}${queryString}`, options).json()
}

private static async getOclConcept(conceptCode: string): Promise<any> {
const options = { timeout: config.get('bwConfig:requestTimeout') | 1000 }

return got
.get(
`${config.get('bwConfig:oclUrl')}/orgs/I-TECH-UW/sources/IPMSLAB/concepts/${conceptCode}`,
options,
)
.json()
}
}

0 comments on commit 3667a80

Please sign in to comment.