Skip to content

Commit

Permalink
feat(#114): fix bundle format
Browse files Browse the repository at this point in the history
  • Loading branch information
witash committed May 27, 2024
1 parent 613bf6b commit 6d03a4f
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 64 deletions.
30 changes: 18 additions & 12 deletions mediator/src/controllers/cht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,50 @@ export async function createPatient(chtPatientDoc: any) {

const fhirPatient = buildFhirPatientFromCht(chtPatientDoc.doc);
// create or update in the FHIR Server
// note that either way, its a PUT with the id from the patient doc
// even for create, sends a PUT request
return updateFhirResource({ ...fhirPatient, resourceType: 'Patient' });
}

export async function updatePatientIds(chtFormDoc: any) {
// first, get the existing patient from fhir server
const response = await getFHIRPatientResource(chtFormDoc.openmrs_patient_uuid);
const response = await getFHIRPatientResource(chtFormDoc.external_id);

if (response.status != 200) {
return { status: 500, data: { message: `FHIR responded with ${response.status}`} };
} else if (response.data.total == 0){
return { status: 404, data: { message: `Patient not found`} };
// in case the patient is not found, return 200 to prevent retries
return { status: 200, data: { message: `Patient not found`} };
}

const fhirPatient = response.data.entry[0].resource;
addId(fhirPatient, chtPatientIdentifierType, chtFormDoc.patient_id);

// now, we need to get the actual patient doc from cht...
const patient_uuid = await getPatientUUIDFromSourceId(chtFormDoc._id);
addId(fhirPatient, chtDocumentIdentifierType, patient_uuid);

return updateFhirResource({ ...fhirPatient, resourceType: 'Patient' });
if (patient_uuid){
addId(fhirPatient, chtDocumentIdentifierType, patient_uuid);
return updateFhirResource({ ...fhirPatient, resourceType: 'Patient' });
} else {
// in case the patient is not found, return 200 to prevent retries
return { status: 200, data: { message: `Patient not found`} };
}
}

export async function createEncounter(chtReport: any) {
const fhirEncounter = buildFhirEncounterFromCht(chtReport);
const response = await updateFhirResource(fhirEncounter);

const bundle: fhir4.Bundle = {
resourceType: 'Bundle',
type: 'collection',
entry: [fhirEncounter]
if (response.status != 200 && response.status != 201){
// in case of an error from fhir server, return it to caller
return response;
}

for (const entry of chtReport.observations) {
if (entry.valueCode || entry.valueString || entry.valueDateTime) {
const observation = buildFhirObservationFromCht(chtReport.patient_uuid, fhirEncounter, entry);
bundle.entry?.push(observation);
updateFhirResource(observation);
}
}
return createFhirResource(bundle);

return { status: 200, data: {} };
}
2 changes: 1 addition & 1 deletion mediator/src/mappers/cht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function buildFhirPatientFromCht(chtPatient: any): fhir4.Patient {
telecom: [phone]
};

copyIdToNamedIdentifier(patient, chtDocumentIdentifierType);
copyIdToNamedIdentifier(patient, patient, chtDocumentIdentifierType);

return patient;
}
Expand Down
14 changes: 11 additions & 3 deletions mediator/src/mappers/openmrs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const visitType: fhir4.CodeableConcept = {
text: "Home Visit",
coding: [{
system: "http://fhir.openmrs.org/code-system/visit-type",
code: "d66e9fe0-7d51-4801-a550-5d462ad1c944",
code: "7b0f5697-27e3-40c4-8bae-f4049abfb4ed",
display: "Home Visit",
}]
}
Expand All @@ -35,15 +35,22 @@ Build an OpenMRS Visit w/ Visit Note
From a fhir Encounter
One CHT encounter will become 2 OpenMRS Encounters
*/
export function buildOpenMRSVisit(fhirEncounter: fhir4.Encounter): fhir4.Encounter[] {
export function buildOpenMRSVisit(patientId: string, fhirEncounter: fhir4.Encounter): fhir4.Encounter[] {
const openMRSVisit = fhirEncounter;
openMRSVisit.type = [visitType]
//openMRSVisit.subject.reference = `Patient/${patient_id}`

const subjectRef: fhir4.Reference = {
reference: `Patient/${patientId}`,
type: "Patient"
};

openMRSVisit.subject = subjectRef;

const visitRef: fhir4.Reference = {
reference: `Encounter/${openMRSVisit.id}`,
type: "Encounter"
};

const openMRSVisitNote: fhir4.Encounter = {
...openMRSVisit,
id: randomUUID(),
Expand Down Expand Up @@ -82,6 +89,7 @@ export function buildOpenMRSPatient(fhirPatient: fhir4.Patient): fhir4.Patient {
}
fhirPatient.name = fhirPatient.name?.map(addId);
fhirPatient.identifier = fhirPatient.identifier?.map(addId);
fhirPatient.telecom = fhirPatient.telecom?.map(addId);
return fhirPatient;
}

10 changes: 10 additions & 0 deletions mediator/src/routes/cht.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Router } from 'express';
import { requestHandler } from '../utils/request';
import { createPatient, updatePatientIds, createEncounter } from '../controllers/cht'
import { syncPatients, syncEncountersAndObservations} from '../utils/openmrs_sync'

const router = Router();

Expand All @@ -21,4 +22,13 @@ router.post(
requestHandler((req) => createEncounter(req.body))
);

router.post(
'/sync',
requestHandler(async (req) => {
await syncPatients();
syncEncountersAndObservations();
return { status: 200, data: {}};
})
);

export default router;
5 changes: 4 additions & 1 deletion mediator/src/routes/tests/cht.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import request from 'supertest';
import app from '../../..';
import { ChtPatientFactory, ChtPregnancyForm } from '../../middlewares/schemas/tests/cht-request-factories';
import * as fhir from '../../utils/fhir';
import axios from 'axios';

jest.mock('axios');

describe('POST /cht/patient', () => {
it('accepts incoming request with valid patient resource', async () => {
Expand Down Expand Up @@ -44,6 +47,6 @@ describe('POST /cht/patient', () => {
resourceType: 'Patient',
});
*/
expect(fhir.createFhirResource).toHaveBeenCalled();
expect(fhir.updateFhirResource).toHaveBeenCalled();
});
});
6 changes: 5 additions & 1 deletion mediator/src/utils/cht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ export async function getPatientUUIDFromSourceId(source_id: string) {
}

const patient = await queryCht(query);
return patient.data.docs[0]._id;
if ( patient.data.docs && patient.data.docs.length > 0 ){
return patient.data.docs[0]._id;
} else {
return ''
}
}

export async function createChtPatient(fhirPatient: fhir4.Patient) {
Expand Down
33 changes: 25 additions & 8 deletions mediator/src/utils/fhir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,20 +114,24 @@ export async function getFHIRPatients(lastUpdated: Date) {
);
}

export function copyIdToNamedIdentifier(resource: fhir4.Patient, fromIDType: fhir4.CodeableConcept){
export function copyIdToNamedIdentifier(fromResource: any, toResource: fhir4.Patient | fhir4.Encounter, fromIDType: fhir4.CodeableConcept){
const identifier: fhir4.Identifier = {
type: fromIDType,
value: resource.id
value: fromResource.id,
use: "secondary"
};
resource.identifier?.push(identifier);
return resource;
const sameIdType = (id: any) => (id.type.text === fromIDType.text)
if (!toResource.identifier?.some(sameIdType)) {
toResource.identifier?.push(identifier);
}
return toResource;
}

export function getIdType(resource: fhir4.Patient, idType: fhir4.CodeableConcept): string{
export function getIdType(resource: fhir4.Patient | fhir4.Encounter, idType: fhir4.CodeableConcept): string{
return resource.identifier?.find((id: any) => id?.type.text == idType.text)?.value || '';
}

export function addId(resource: fhir4.Patient, idType: fhir4.CodeableConcept, value: string){
export function addId(resource: fhir4.Patient | fhir4.Encounter, idType: fhir4.CodeableConcept, value: string){
const identifier: fhir4.Identifier = {
type: idType,
value: value
Expand Down Expand Up @@ -171,7 +175,7 @@ export async function updateFhirResource(doc: fhir4.Resource) {
},
});

return { status: res.status, data: res.data };
return { status: res?.status, data: res?.data };
} catch (error: any) {
logger.error(error);
return { status: error.status, data: error.data };
Expand All @@ -184,7 +188,20 @@ export async function getFhirResourcesSince(lastUpdated: Date, resourceType: str
`${FHIR.url}/${resourceType}/?_lastUpdated=gt${lastUpdated.toISOString()}`,
axiosOptions
);
return { status: res.status, data: res.data };
return { status: res?.status, data: res?.data };
} catch (error: any) {
logger.error(error);
return { status: error.status, data: error.data };
}
}

export async function getFhirResource(id: string, resourceType: string) {
try {
const res = await axios.get(
`${FHIR.url}/${resourceType}/${id}`,
axiosOptions
);
return { status: res?.status, data: res?.data };
} catch (error: any) {
logger.error(error);
return { status: error.status, data: error.data };
Expand Down
Loading

0 comments on commit 6d03a4f

Please sign in to comment.