diff --git a/.eslintcache b/.eslintcache index 58926cb..b9f20cc 100644 --- a/.eslintcache +++ b/.eslintcache @@ -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]] \ No newline at end of file +[{"/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]] diff --git a/config/config_docker.json b/config/config_docker.json index 6d8383e..1fb3741 100644 --- a/config/config_docker.json +++ b/config/config_docker.json @@ -6,14 +6,14 @@ "mediator": { "api": { "username": "root@openhim.org", - "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": { @@ -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", diff --git a/config/ipms_facility_mappings.xlsx b/config/ipms_facility_mappings.xlsx index 78eb796..ab5a5bf 100644 Binary files a/config/ipms_facility_mappings.xlsx and b/config/ipms_facility_mappings.xlsx differ diff --git a/config/ipms_facility_mappings_v0.xlsx b/config/ipms_facility_mappings_v0.xlsx new file mode 100644 index 0000000..78eb796 Binary files /dev/null and b/config/ipms_facility_mappings_v0.xlsx differ diff --git a/debug.docker-compose.yml b/debug.docker-compose.yml index 41be92e..ab3c8d9 100644 --- a/debug.docker-compose.yml +++ b/debug.docker-compose.yml @@ -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: diff --git a/src/lib/locationMap.ts b/src/lib/locationMap.ts index 01ffcd1..f21a8be 100644 --- a/src/lib/locationMap.ts +++ b/src/lib/locationMap.ts @@ -8,7 +8,6 @@ type FacilityMapping = { provider: string patientType: string patientStatus: string - futurePatientStatus: string xLocation: string } @@ -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), } }) diff --git a/src/server/__tests__/mllpAdapter.ts b/src/server/__tests__/mllpAdapter.ts index 81ecc6b..fcf1e37 100644 --- a/src/server/__tests__/mllpAdapter.ts +++ b/src/server/__tests__/mllpAdapter.ts @@ -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' diff --git a/src/workflows/labWorkflowsBw.ts b/src/workflows/labWorkflowsBw.ts index 8bbd8a5..c263ecd 100644 --- a/src/workflows/labWorkflowsBw.ts +++ b/src/workflows/labWorkflowsBw.ts @@ -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) @@ -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) } } @@ -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) { @@ -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 @@ -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) { @@ -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 } @@ -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, @@ -999,7 +1028,20 @@ export class LabWorkflowsBw extends LabWorkflows { private static async getOclMapping(queryString: string): Promise { 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 { + 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() + } }