Skip to content

Commit

Permalink
(feat) O3-3316 Add support for encounter diagnosis
Browse files Browse the repository at this point in the history
  • Loading branch information
CynthiaKamau committed Jan 6, 2025
1 parent 3e4e2fa commit f306ec1
Show file tree
Hide file tree
Showing 10 changed files with 472 additions and 3 deletions.
254 changes: 254 additions & 0 deletions src/adapters/encounter-diagnosis-adapter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
import { type FormContextProps } from '../provider/form-provider';
import { type FormField } from '../types';
import { EncounterDiagnosisAdapter } from './encounter-diagnosis-adapter';

const formContext = {
methods: null,
workspaceLayout: 'maximized',
isSubmitting: false,
patient: {
id: '833db896-c1f0-11eb-8529-0242ac130003',
},
formJson: null,
visit: null,
sessionMode: 'enter',
sessionDate: new Date(),
location: {
uuid: '41e6e516-c1f0-11eb-8529-0242ac130003',
},
currentProvider: null,
layoutType: 'small-desktop',
domainObjectValue: {
uuid: '873455da-3ec4-453c-b565-7c1fe35426be',
obs: [],
diagnoses: [],
},
previousDomainObjectValue: null,
processor: null,
formFields: [],
formFieldAdapters: null,
formFieldValidators: null,
customDependencies: {
patientPrograms: [],
},
getFormField: jest.fn(),
addFormField: jest.fn(),
updateFormField: jest.fn(),
removeFormField: () => {},
addInvalidField: jest.fn(),
removeInvalidField: jest.fn(),
setInvalidFields: jest.fn(),
setForm: jest.fn(),
} as FormContextProps;

const field = {
label: 'Test Diagnosis',
id: 'DiagNosIS',
type: 'diagnosis',
questionOptions: {
rendering: 'repeating',
diagnosis: {
rank: 1,
isConfirmed: false,
},
datasource: {
name: 'problem_datasource',
config: {
class: [
'8d4918b0-c2cc-11de-8d13-0010c6dffd0f',
'8d492954-c2cc-11de-8d13-0010c6dffd0f',
'8d492b2a-c2cc-11de-8d13-0010c6dffd0f',
],
},
},
},
meta: {
submission: {
newValue: null,
},
previousValue: null,
},
validators: [
{
type: 'form_field',
},
{
type: 'default_value',
},
],
isHidden: false,
isRequired: false,
isDisabled: false,
} as FormField;

const diagnoses = [
{
uuid: '8d975f9e-e9e6-452f-be7c-0e87c047f056',
diagnosis: {
coded: {
uuid: '127133AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
display: 'Schistosoma Mansonii Infection',
links: [],
},
},
condition: null,
encounter: {
uuid: '9a4b06bd-d655-414f-b9ce-69e940c337ce',
},
certainty: 'CONFIRMED',
rank: 1,
voided: false,
display: 'Schistosoma Mansonii Infection',
patient: {
uuid: '00affa97-0010-417c-87f5-de48362de915',
display: '1000VKV - Bett Tett',
},
formFieldNamespace: 'rfe-forms',
formFieldPath: 'rfe-forms-DiagNosIS_1',
links: [],
resourceVersion: '1.8',
},
{
uuid: 'b2d0e95b-d2f6-49d1-a477-acc7026edbd7',
diagnosis: {
coded: {
uuid: '137329AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
display: 'Infection due to Entamoeba Histolytica',
links: [],
},
},
condition: null,
encounter: {
uuid: '9a4b06bd-d655-414f-b9ce-69e940c337ce',
},
certainty: 'PROVISIONAL',
rank: 1,
voided: false,
display: 'Infection due to Entamoeba Histolytica',
patient: {
uuid: '00affa97-0010-417c-87f5-de48362de915',
display: '1000VKV - Bett Tett',
},
formFieldNamespace: 'rfe-forms',
formFieldPath: 'rfe-forms-DiagNosIS',
links: [],
resourceVersion: '1.8',
},
];

describe('EncounterDiagnosisAdapter', () => {
it('should should handle submission of a diagnosis field', async () => {
const value = '127133AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
EncounterDiagnosisAdapter.transformFieldValue(field, value, formContext);
expect(field.meta.submission.newValue).toEqual({
patient: '833db896-c1f0-11eb-8529-0242ac130003',
condition: null,
diagnosis: {
coded: '127133AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
},
certainty: 'PROVISIONAL',
rank: 1,
formFieldPath: 'rfe-forms-DiagNosIS',
formFieldNamespace: 'rfe-forms',
});
});

it('should get initial value for the diagnosis', async () => {
formContext.domainObjectValue.diagnoses.push(...diagnoses);
const diagnosis = await EncounterDiagnosisAdapter.getInitialValue(field, null, formContext);
expect(diagnosis).toEqual('137329AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
});

it('should return null for getPreviousValue', async () => {
const previousValue = await EncounterDiagnosisAdapter.getPreviousValue(field, null, formContext);
expect(previousValue).toBeNull();
});

it('should execute tearDown without issues', () => {
expect(() => EncounterDiagnosisAdapter.tearDown()).not.toThrow();
});

it('should edit a diagnosis value', () => {
formContext.sessionMode = 'edit';

const value = '128138AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
field.meta = {
previousValue: {
patient: '833db896-c1f0-11eb-8529-0242ac130003',
condition: null,
diagnosis: {
coded: '127133AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
},
certainty: 'PROVISIONAL',
rank: 1,
formFieldPath: 'rfe-forms-DiagNosIS',
formFieldNamespace: 'rfe-forms',
uuid: '0e20bb67-5d7f-41e0-96a1-751efc21a96f',
},
initialValue: {
omrsObject: {
uuid: '0e20bb67-5d7f-41e0-96a1-751efc21a96f',
diagnosis: {
coded: {
uuid: '127133AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
display: 'Schistosoma Mansonii Infection',
},
},
},
refinedValue: null,
},
};

EncounterDiagnosisAdapter.transformFieldValue(field, value, formContext);
expect(field.meta.submission.newValue).toEqual({
patient: '833db896-c1f0-11eb-8529-0242ac130003',
condition: null,
diagnosis: {
coded: '128138AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
},
certainty: 'PROVISIONAL',
rank: 1,
formFieldPath: 'rfe-forms-DiagNosIS',
formFieldNamespace: 'rfe-forms',
uuid: '0e20bb67-5d7f-41e0-96a1-751efc21a96f',
});
expect(field.meta.submission.voidedValue).toBe(undefined);
});

it('should handle deleting a diagnosis', () => {
formContext.sessionMode = 'edit';

const value = null;
field.meta = {
previousValue: {
patient: '833db896-c1f0-11eb-8529-0242ac130003',
condition: null,
diagnosis: {
coded: '127133AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
},
certainty: 'PROVISIONAL',
rank: 1,
formFieldPath: 'rfe-forms-DiagNosIS',
formFieldNamespace: 'rfe-forms',
uuid: '0e20bb67-5d7f-41e0-96a1-751efc21a96f',
},
initialValue: {
omrsObject: {
uuid: '0e20bb67-5d7f-41e0-96a1-751efc21a96f',
diagnosis: {
coded: {
uuid: '127133AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
display: 'Schistosoma Mansonii Infection',
},
},
},
refinedValue: null,
},
repeat: {
wasDeleted: true,
},
};
EncounterDiagnosisAdapter.transformFieldValue(field, value, formContext);
expect(field.meta.submission.voidedValue).toEqual({ uuid: '0e20bb67-5d7f-41e0-96a1-751efc21a96f', voided: true });
});
});
95 changes: 95 additions & 0 deletions src/adapters/encounter-diagnosis-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { type OpenmrsResource } from '@openmrs/esm-framework';
import { type DiagnosisPayload, type FormFieldValueAdapter, type FormProcessorContextProps } from '../types';
import { type FormContextProps } from '../provider/form-provider';
import { type OpenmrsEncounter, type FormField } from '../types';
import { clearSubmission, gracefullySetSubmission } from '../utils/common-utils';

export let assignedDiagnosesIds: string[] = [];

export const EncounterDiagnosisAdapter: FormFieldValueAdapter = {
transformFieldValue: function (field: FormField, value: any, context: FormContextProps) {
if (context.sessionMode == 'edit' && field.meta?.previousValue?.uuid) {
return editDiagnosis(value, field, context.patient.id);
}
const newValue = constructNewDiagnosis(value, field, context.patient.id);
gracefullySetSubmission(field, newValue, null);
return newValue;
},
getInitialValue: function (
field: FormField,
sourceObject: OpenmrsResource,
context: FormProcessorContextProps,
): Promise<any> {
const encounter = sourceObject ?? (context.domainObjectValue as OpenmrsEncounter);
const matchedDiagnosis = encounter.diagnoses.find(
(diagnosis) => diagnosis.formFieldPath === `rfe-forms-${field.id}`,
);

if (matchedDiagnosis) {
field.meta = { ...(field.meta || {}), previousValue: matchedDiagnosis };
if (!assignedDiagnosesIds.includes(matchedDiagnosis.diagnosis?.coded?.uuid)) {
assignedDiagnosesIds.push(matchedDiagnosis.diagnosis?.coded?.uuid);
}
return matchedDiagnosis.diagnosis?.coded.uuid;
}
return null;
},
getPreviousValue: function (
field: FormField,
sourceObject: OpenmrsResource,
context: FormProcessorContextProps,
): Promise<any> {
return null;
},
getDisplayValue: (field: FormField, value: any) => {
return field.questionOptions.answers?.find((option) => option.concept == value)?.label || value;
},
tearDown: function (): void {
assignedDiagnosesIds = [];
},
};

const constructNewDiagnosis = (value: any, field: FormField, patientUuid: string, uuid?: string) => {
if (!value) {
return null;
}
const diagnosis: DiagnosisPayload = {
patient: patientUuid,
condition: null,
diagnosis: {
coded: value,
},
certainty: field.questionOptions?.diagnosis?.isConfirmed ? 'CONFIRMED' : 'PROVISIONAL',
rank: field.questionOptions.diagnosis?.rank ?? 1, // rank 1 denotes a diagnosis is primary, else secondary
formFieldPath: `rfe-forms-${field.id}`,
formFieldNamespace: 'rfe-forms',
};

if (uuid && uuid.trim() !== '') {
diagnosis.uuid = uuid;
}

return diagnosis;
};

function editDiagnosis(newEncounterDiagnosis: any, field: FormField, patientUuid: string) {
if (newEncounterDiagnosis === field.meta.previousValue?.diagnosis?.coded?.uuid && !field.meta.repeat?.wasDeleted) {
clearSubmission(field);
return null;
}

//the field has been deleted
if (field.meta.repeat?.wasDeleted) {
const voided = {
uuid: field.meta.previousValue?.uuid,
voided: true,
};
gracefullySetSubmission(field, constructNewDiagnosis(newEncounterDiagnosis, field, null), voided);
return field.meta.submission.newValue || null;
} else {
const oldDiagnosis = field.meta.initialValue?.omrsObject as OpenmrsResource;
const newValue = constructNewDiagnosis(newEncounterDiagnosis, field, patientUuid, oldDiagnosis.uuid);
gracefullySetSubmission(field, newValue, null);
return newValue;
}
}
1 change: 1 addition & 0 deletions src/components/repeat/repeat.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useFormFactory } from '../../provider/form-factory-provider';
const renderingByTypeMap: Record<string, RenderType> = {
obsGroup: 'group',
testOrder: 'select',
diagnosis: 'ui-select-extended',
};

const Repeat: React.FC<FormFieldInputProps> = ({ field }) => {
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const encounterRepresentation =
'custom:(uuid,encounterDatetime,encounterType:(uuid,name,description),location:(uuid,name),' +
'patient:(uuid,display),encounterProviders:(uuid,provider:(uuid,name),encounterRole:(uuid,name)),' +
'orders:(uuid,display,concept:(uuid,display),voided),' +
'diagnoses:(uuid,certainty,condition,formFieldPath,formFieldNamespace,display,rank,voided,diagnosis:(coded:(uuid,display))),' +
'obs:(uuid,obsDatetime,comment,voided,groupMembers,formFieldNamespace,formFieldPath,concept:(uuid,name:(uuid,name)),value:(uuid,name:(uuid,name),' +
'names:(uuid,conceptNameType,name))))';
export const FormsStore = 'forms-engine-store';
Expand Down
16 changes: 13 additions & 3 deletions src/processors/encounter/encounter-form-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,21 @@ export class EncounterFormProcessor extends FormProcessor {
// save encounter
try {
const { data: savedEncounter } = await saveEncounter(abortController, encounter, encounter.uuid);
const saveOrders = savedEncounter.orders.map((order) => order.orderNumber);
if (saveOrders.length) {
const savedOrders = savedEncounter.orders.map((order) => order.orderNumber);
const savedDiagnoses = savedEncounter.diagnoses.map((diagnosis) => diagnosis.display);
if (savedOrders.length) {
showSnackbar({
title: translateFn('ordersSaved', 'Order(s) saved successfully'),
subtitle: saveOrders.join(', '),
subtitle: savedOrders.join(', '),
kind: 'success',
isLowContrast: true,
});
}
// handle diagnoses
if (savedDiagnoses.length) {
showSnackbar({
title: translateFn('diagnosisSaved', 'Diagnosis(es) saved successfully'),
subtitle: savedDiagnoses.join(', '),
kind: 'success',
isLowContrast: true,
});
Expand Down
Loading

0 comments on commit f306ec1

Please sign in to comment.