diff --git a/mediator/index.ts b/mediator/index.ts index 767af50a..46cdb7d8 100644 --- a/mediator/index.ts +++ b/mediator/index.ts @@ -8,6 +8,8 @@ import serviceRequestRoutes from './src/routes/service-request'; import encounterRoutes from './src/routes/encounter'; import organizationRoutes from './src/routes/organization'; import endpointRoutes from './src/routes/endpoint'; +import chtRoutes from './src/routes/cht'; +import openmrsRoutes from './src/routes/openmrs'; import { registerMediatorCallback } from './src/utils/openhim'; import os from 'os'; @@ -24,12 +26,18 @@ app.get('*', (_: Request, res: Response) => { res.send({status: 'success', osuptime: osUptime, processuptime: processUptime}); }); +// routes for valid fhir resources app.use('/patient', patientRoutes); app.use('/service-request', serviceRequestRoutes); app.use('/encounter', encounterRoutes); app.use('/organization', organizationRoutes); app.use('/endpoint', endpointRoutes); +// routes for cht docs +app.use('/cht', chtRoutes); +// routes for openmrs +app.use('/openmrs', openmrsRoutes); + if (process.env.NODE_ENV !== 'test') { app.listen(PORT, () => logger.info(`Server listening on port ${PORT}`)); diff --git a/mediator/src/utils/cht.ts b/mediator/src/utils/cht.ts index e0c96000..680ed8b9 100644 --- a/mediator/src/utils/cht.ts +++ b/mediator/src/utils/cht.ts @@ -3,6 +3,7 @@ import { CHT } from '../../config'; import { generateBasicAuthUrl } from './url'; import https from 'https'; import path from 'path'; +import { logger } from '../../logger'; export async function createChtRecord(patientId: string) { const record = { @@ -22,6 +23,60 @@ export async function createChtRecord(patientId: string) { return await axios.post(chtApiUrl, record, options); } +export async function createChtPatient(fhirPatient: fhir4.Patient) { + const options = { + httpsAgent: new https.Agent({ + rejectUnauthorized: false, + }), + }; + + const name = fhirPatient.name?.[0] + const given = name?.given ? name?.given : '' + const tc = fhirPatient.telecom?.[0] + const record = { + _meta: { + form: "N" + }, + age_in_years: 22, + patient_phone: tc?.value, + patient_name: `${given} ${name?.family}`, + gender: fhirPatient.gender, + location_id: "65985" + } + + const chtApiUrl = generateChtRecordsApiUrl(CHT.url, CHT.username, CHT.password); + + return await axios.post(chtApiUrl, record, options); +} + +export async function chtRecordFromObservations(patient_id: string, observations: any) { + const options = { + httpsAgent: new https.Agent({ + rejectUnauthorized: false, + }), + }; + + const record: any = { + _meta: { + form: "openmrs_anc" + }, + patient_id: patient_id + } + + for (const entry of observations.entry) { + const code:string = entry.resource.code.coding[0].code; + if (entry.resource.valueCodeableConcept) { + record[code] = entry.resource.valueCodeableConcept.text; + } else if (entry.resource.valueDateTime) { + record[code] = entry.resource.valueDateTime; + } + } + + const chtApiUrl = generateChtRecordsApiUrl(CHT.url, CHT.username, CHT.password); + + return await axios.post(chtApiUrl, record, options); +} + export const generateChtRecordsApiUrl = (chtUrl: string, username: string, password: string) => { const endpoint = generateBasicAuthUrl(chtUrl, username, password); return path.join(endpoint, '/api/v2/records'); diff --git a/mediator/src/utils/openmrs.ts b/mediator/src/utils/openmrs.ts index f6a5e518..333da8e7 100644 --- a/mediator/src/utils/openmrs.ts +++ b/mediator/src/utils/openmrs.ts @@ -16,6 +16,123 @@ const chtIdentifierType: fhir4.CodeableConcept = { text: 'CHT ID' } +const medicIdentifierType: fhir4.CodeableConcept = { + text: 'Medic ID' +} + +const noteEncounterType: fhir4.CodeableConcept = { + text: "Visit Note", + coding: [{ + system: "http://fhir.openmrs.org/code-system/encounter-type", + code: "d7151f82-c1f3-4152-a605-2f9ea7414a79", + display: "Visit Note" + }] +} + +const chwEncounterType: fhir4.CodeableConcept = { + text: "Community Health Worker Visit", + coding: [{ + system: "http://fhir.openmrs.org/code-system/visit-type", + code: "479a14c9-fd05-4399-8e3d-fed3f8c654c", + display: "Community Health Worker Visit", + }] +} + +const homeEncounterType: fhir4.CodeableConcept = { + text: "Home Visit", + coding: [{ + system: "http://fhir.openmrs.org/code-system/visit-type", + code: "d66e9fe0-7d51-4801-a550-5d462ad1c944", + display: "Home Visit", + }] +} + +const homeHealthEncounterClass: fhir4.CodeableConcept = { + text: 'HH', + coding : [{ + system: "http://terminology.hl7.org/CodeSystem/v3-ActCode", + code: "HH" + }] +} + +export function buildOpenMRSVisit(patient_id: string, reported_date: number) : fhir4.Encounter { + const visit = buildOpenMRSEncounter(patient_id, reported_date, homeEncounterType); + return visit; +} + +export function buildOpenMRSVisitNote(patient_id: string, reported_date: number, visit_id: string): fhir4.Encounter { + const visitNote = buildOpenMRSEncounter(patient_id, reported_date, noteEncounterType); + const visitRef: fhir4.Reference = { + reference: `Encounter/${visit_id}`, + type: "Encounter" + }; + visitNote.partOf = visitRef; + return visitNote; +} + +export function buildOpenMRSEncounter(patient_id: string, reported_date: number, visitType: fhir4.CodeableConcept): fhir4.Encounter { + const patientRef: fhir4.Reference = { + reference: `Patient/${patient_id}`, + type: "Patient" + }; + + const openMRSEncounter: fhir4.Encounter = { + resourceType: 'Encounter', + id: randomUUID(), + status: 'unknown', + class: homeHealthEncounterClass, + type: [visitType], + subject: patientRef, + period: { + start: new Date(reported_date).toISOString(), + end: new Date(reported_date + 1000*60*10).toISOString() + } + } + + return openMRSEncounter +} + + +export function buildOpenMRSObservation(patient_id: string, encounter_id: string, entry: any): fhir4.Observation { + const patientRef: fhir4.Reference = { + reference: `Patient/${patient_id}`, + type: "Patient" + }; + + const encounterRef: fhir4.Reference = { + reference: `Encounter/${encounter_id}`, + type: "Encounter" + }; + + const observation: fhir4.Observation = { + resourceType: "Observation", + subject: patientRef, + encounter: encounterRef, + status: "final", + code: { + coding: [{ + code: entry.code, + }], + }, + effectiveDateTime: "2024-03-31T12:26:27+00:00", + issued: "2024-03-31T12:26:28.000+00:00", + }; + + if ('valueCode' in entry){ + observation.valueCodeableConcept = { + coding: [{ + code: entry['valueCode'] + }] + }; + } else if ('valueDateTime' in entry){ + observation.valueDateTime = entry['valueDateTime']; + } else if ('valueString' in entry){ + observation.valueString = entry['valueString']; + } + + return observation; +} + export function buildOpenMRSPatient(chtPatient: Record): fhir4.Patient { const nameParts = chtPatient.name.split(" "); const familyName = nameParts.pop() || ""; @@ -34,10 +151,18 @@ export function buildOpenMRSPatient(chtPatient: Record): fhir4.Pati use: 'usual' }; + const medicIdentifier: OpenMRSIdentifier = { + id: randomUUID(), + type: medicIdentifierType, + value: chtPatient.patient_id, + system: 'cht', + use: 'official' + }; + const chtIdentifier: OpenMRSIdentifier = { id: randomUUID(), type: chtIdentifierType, - value: chtPatient.id, + value: chtPatient._id, system: 'cht', use: 'official' }; @@ -46,8 +171,8 @@ export function buildOpenMRSPatient(chtPatient: Record): fhir4.Pati resourceType: 'Patient', name: [name], birthDate: chtPatient.birthDate, - id: chtPatient.id, - identifier: [phoneIdentifier, chtIdentifier], + id: chtPatient._id, + identifier: [phoneIdentifier, chtIdentifier, medicIdentifier], gender: chtPatient.gender };