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 Sep 25, 2024
1 parent b0ee6ed commit e5de3c9
Show file tree
Hide file tree
Showing 13 changed files with 181 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"access": "public"
},
"dependencies": {
"@carbon/react": ">1.47.0 <1.50.0",
"@carbon/react": "1.40.0",
"classnames": "^2.5.1",
"lodash-es": "^4.17.21",
"react-error-boundary": "^4.0.13",
Expand Down
78 changes: 78 additions & 0 deletions src/adapters/encounter-diagnosis-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { type OpenmrsResource } from '@openmrs/esm-framework';
import { type FormFieldValueAdapter, type FormProcessorContextProps } from '..';
import { type FormContextProps } from '../provider/form-provider';
import { type OpenmrsEncounter, type FormField } from '../types';
import { clearSubmission, gracefullySetSubmission } from '../utils/common-utils';

export let assignedEncounterDiagnosisIds: 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);
}
const newValue = constructNewDiagnosis(value, field, context.patient.id);
gracefullySetSubmission(field, newValue, null);
console.log('====newValue diagnosis', newValue);

Check failure on line 16 in src/adapters/encounter-diagnosis-adapter.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
return newValue;
},
getInitialValue: function (
field: FormField,
sourceObject: OpenmrsResource,
context: FormProcessorContextProps,
): Promise<any> {
const availableDiagnoses = sourceObject ?? (context.domainObjectValue as OpenmrsEncounter);
const matchedDiagnosis = availableDiagnoses.diagnoses?.find(
(diagnosis) => diagnosis.formFieldPath === `rfe-forms-${field.id}`,
);
if (matchedDiagnosis) {
field.meta = { ...(field.meta || {}), previousValue: matchedDiagnosis };
assignedEncounterDiagnosisIds.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 {
assignedEncounterDiagnosisIds = [];
},
};

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

function editDiagnosis(newEncounterDiagnosis: any, field: FormField) {
if (newEncounterDiagnosis === field.meta.previousValue?.concept?.uuid) {
clearSubmission(field);
return null;
}
const voided = {
uuid: field.meta.previousValue?.uuid,
voided: true,
};
gracefullySetSubmission(field, constructNewDiagnosis(newEncounterDiagnosis, field, null), voided);
return field.meta.submission.newValue || null;
}
1 change: 1 addition & 0 deletions src/adapters/orders-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const OrdersAdapter: FormFieldValueAdapter = {
}
const newValue = constructNewOrder(value, field, context.currentProvider.uuid);
gracefullySetSubmission(field, newValue, null);
console.log('====newValue', newValue);

Check failure on line 18 in src/adapters/orders-adapter.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
return newValue;
},
getInitialValue: function (
Expand Down
2 changes: 1 addition & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { fhirBaseUrl, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
import { encounterRepresentation } from '../constants';
import { type OpenmrsForm, type PatientIdentifier, type PatientProgramPayload } from '../types';
import { type DiagnosisPayload, type OpenmrsForm, type PatientIdentifier, type PatientProgramPayload } from '../types';
import { isUuid } from '../utils/boolean-utils';

export function saveEncounter(abortController: AbortController, payload, encounterUuid?: string) {
Expand Down
5 changes: 5 additions & 0 deletions src/components/repeat/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import { isEmpty } from '../../validators/form-validator';
import { clearSubmission } from '../../utils/common-utils';

export function cloneRepeatField(srcField: FormField, value: OpenmrsResource, idSuffix: number) {
// console.log('====srcField', srcField);
// console.log('====value', value);
// console.log('====idSuffix', idSuffix);

const originalGroupMembersIds: string[] = [];
const clonedField = cloneDeep(srcField) as FormField;
// console.log('====clonedField', clonedField);
clonedField.questionOptions.repeatOptions = { ...(clonedField.questionOptions.repeatOptions ?? {}) };
clonedField.meta = { repeat: { ...(clonedField.meta ?? {}), isClone: true }, previousValue: value };
clonedField.id = `${clonedField.id}_${idSuffix}`;
Expand Down
1 change: 1 addition & 0 deletions src/components/repeat/repeat.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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
10 changes: 10 additions & 0 deletions src/processors/encounter/encounter-form-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export class EncounterFormProcessor extends FormProcessor {
try {
const { data: savedEncounter } = await saveEncounter(abortController, encounter, encounter.uuid);
const saveOrders = savedEncounter.orders.map((order) => order.orderNumber);
const saveDiagnosis = savedEncounter.diagnoses.map((diagnosis) => diagnosis.display);
if (saveOrders.length) {
showSnackbar({
title: translateFn('ordersSaved', 'Order(s) saved successfully'),
Expand All @@ -174,6 +175,15 @@ export class EncounterFormProcessor extends FormProcessor {
isLowContrast: true,
});
}
// handle diagnoses
if (saveDiagnosis.length) {
showSnackbar({
title: translateFn('diagnosisSaved', 'Diagnosis(s) saved successfully'),
subtitle: saveDiagnosis.join(', '),
kind: 'success',
isLowContrast: true,
});
}
// handle attachments
try {
const attachmentsResponse = await Promise.all(
Expand Down
31 changes: 31 additions & 0 deletions src/processors/encounter/encounter-processor-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ConceptTrue } from '../../constants';
import { DefaultValueValidator } from '../../validators/default-value-validator';
import { cloneRepeatField } from '../../components/repeat/helpers';
import { assignedOrderIds } from '../../adapters/orders-adapter';
import { assignedEncounterDiagnosisIds } from '../../adapters/encounter-diagnosis-adapter';

export function prepareEncounter(
context: FormContextProps,
Expand All @@ -28,6 +29,7 @@ export function prepareEncounter(
const obsForSubmission = [];
prepareObs(obsForSubmission, formFields);
const ordersForSubmission = prepareOrders(formFields);
const diagnosisForSubmission = prepareDiagnosis(formFields);
let encounterForSubmission: OpenmrsEncounter = {};

if (encounter) {
Expand Down Expand Up @@ -57,6 +59,7 @@ export function prepareEncounter(
}
encounterForSubmission.obs = obsForSubmission;
encounterForSubmission.orders = ordersForSubmission;
encounterForSubmission.diagnoses = diagnosisForSubmission;
} else {
encounterForSubmission = {
patient: patient.id,
Expand All @@ -75,6 +78,7 @@ export function prepareEncounter(
},
visit: visit?.uuid,
orders: ordersForSubmission,
diagnoses: diagnosisForSubmission,
};
}
return encounterForSubmission;
Expand Down Expand Up @@ -300,6 +304,27 @@ export async function hydrateRepeatField(
}),
);
}

//handle diagnoses
const unMappedDiagnosis = encounter.diagnoses.filter((diagnosis) => {
return !assignedEncounterDiagnosisIds.includes(diagnosis.diagnosis.coded.uuid);
});

if (field.type === 'diagnosis') {
return Promise.all(
unMappedDiagnosis
.filter((diagnosis) => !diagnosis.voided)
.map(async (diagnosis) => {
const clone = cloneRepeatField(field, diagnosis, counter++);
initialValues[clone.id] = await formFieldAdapters[field.type].getInitialValue(
clone,
{ diagnoses: [diagnosis] } as any,
context,
);
return clone;
}),
);
}
// handle obs groups
return Promise.all(
unMappedGroups.map(async (group) => {
Expand All @@ -318,3 +343,9 @@ export async function hydrateRepeatField(
}),
).then((results) => results.flat());
}

function prepareDiagnosis(fields: FormField[]) {
return fields
.filter((field) => field.type === 'diagnosis' && hasSubmission(field))
.map((field) => field.meta.submission.newValue);
}
5 changes: 5 additions & 0 deletions src/registry/inbuilt-components/inbuiltFieldValueAdapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ObsCommentAdapter } from '../../adapters/obs-comment-adapter';
import { OrdersAdapter } from '../../adapters/orders-adapter';
import { PatientIdentifierAdapter } from '../../adapters/patient-identifier-adapter';
import { ProgramStateAdapter } from '../../adapters/program-state-adapter';
import { EncounterDiagnosisAdapter } from '../../adapters/encounter-diagnosis-adapter';
import { type FormFieldValueAdapter } from '../../types';

export const inbuiltFieldValueAdapters: RegistryItem<FormFieldValueAdapter>[] = [
Expand Down Expand Up @@ -61,4 +62,8 @@ export const inbuiltFieldValueAdapters: RegistryItem<FormFieldValueAdapter>[] =
type: 'patientIdentifier',
component: PatientIdentifierAdapter,
},
{
type: 'diagnosis',
component: EncounterDiagnosisAdapter,
},
];
19 changes: 19 additions & 0 deletions src/transformers/default-schema-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ function transformByType(question: FormField) {
? 'date'
: question.questionOptions.rendering;
break;
case 'diagnosis':
handleDiagnosesDataSource(question);
break;
}
}

Expand Down Expand Up @@ -237,3 +240,19 @@ function handleQuestionsWithObsComments(sectionQuestions: Array<FormField>): Arr

return augmentedQuestions;
}

function handleDiagnosesDataSource(question: FormField) {
if ('dataSource' in question.questionOptions && question.questionOptions['dataSource'] === 'diagnoses') {
question.questionOptions.datasource = {
name: 'problem_datasource',
config: {
class: [
'8d4918b0-c2cc-11de-8d13-0010c6dffd0f',
'8d492954-c2cc-11de-8d13-0010c6dffd0f',
'8d492b2a-c2cc-11de-8d13-0010c6dffd0f',
],
},
};
delete question.questionOptions['dataSource'];
}
}
27 changes: 27 additions & 0 deletions src/types/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface OpenmrsEncounter {
visit?: OpenmrsResource | string;
encounterProviders?: Array<Record<string, any>>;
form?: OpenmrsFormResource;
diagnoses?: Array<Diagnosis>;
}

export interface OpenmrsObs extends OpenmrsResource {
Expand Down Expand Up @@ -127,3 +128,29 @@ export interface PatientIdentifier {
location?: string;
preferred?: boolean;
}

export interface DiagnosisPayload {
encounter: string;
patient: string;
condition: null;
diagnosis: {
coded: string;
};
certainty: string;
rank: number;
}

export interface Diagnosis {
encounter: string;
patient: string;
diagnosis: {
coded: {
uuid: string;
};
};
certainty: string;
rank: number;
display: string;
voided: boolean;
uuid: string;
}
1 change: 1 addition & 0 deletions src/types/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export interface FormQuestionOptions {
comment?: string;
orientation?: 'vertical' | 'horizontal';
shownCommentOptions?: { validators?: Array<Record<string, any>>; hide?: { hideWhenExpression: string } };
rank?: number;
}

export interface QuestionAnswerOption {
Expand Down

0 comments on commit e5de3c9

Please sign in to comment.