Skip to content

Commit

Permalink
Simplify the ExpressionRunner's logic and enhance support for custom …
Browse files Browse the repository at this point in the history
…data sources (#47)
  • Loading branch information
samuelmale authored Apr 25, 2023
1 parent ab7221d commit e9a87ae
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 202 deletions.
15 changes: 8 additions & 7 deletions src/components/encounter/ohri-encounter-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import OHRIFormPage from '../page/ohri-form-page';
import { InstantEffect } from '../../utils/instant-effect';
import { FormSubmissionHandler } from '../../ohri-form.component';
import { isTrue } from '../../utils/boolean-utils';
import { evaluateExpression } from '../../utils/expression-runner';
import { evaluateAsyncExpression, evaluateExpression } from '../../utils/expression-runner';
import { getPreviousEncounter, saveEncounter } from '../../api/api';
import { scrollIntoView } from '../../utils/ohri-sidebar';
import { useEncounter } from '../../hooks/useEncounter';
Expand Down Expand Up @@ -458,7 +458,7 @@ export const OHRIEncounterForm: React.FC<OHRIEncounterFormProps> = ({
const dependant = fields.find(f => f.id == dep);
// evaluate calculated value
if (!dependant.isHidden && dependant.questionOptions.calculate?.calculateExpression) {
let result = evaluateExpression(
evaluateAsyncExpression(
dependant.questionOptions.calculate.calculateExpression,
{ value: dependant, type: 'field' },
fields,
Expand All @@ -467,11 +467,12 @@ export const OHRIEncounterForm: React.FC<OHRIEncounterFormProps> = ({
mode: sessionMode,
patient,
},
);
result = isEmpty(result) ? '' : result;
values[dependant.id] = result;
setFieldValue(dependant.id, result);
getHandler(dependant.type).handleFieldSubmission(dependant, result, encounterContext);
).then(result => {
result = isEmpty(result) ? '' : result;
values[dependant.id] = result;
setFieldValue(dependant.id, result);
getHandler(dependant.type).handleFieldSubmission(dependant, result, encounterContext);
});
}
// evaluate hide
if (dependant.hide) {
Expand Down
146 changes: 19 additions & 127 deletions src/utils/common-expression-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
'use ';
import moment from 'moment';
import { OHRIFormField } from '../api/types';
import { FormNode } from './expression-runner';
Expand All @@ -12,6 +11,7 @@ export class CommonExpressionHelpers {
allFieldValues: Record<string, any> = {};
allFieldsKeys: string[] = [];
api = apiFunctions;
isEmpty = isValueEmpty;

constructor(
node: FormNode,
Expand All @@ -27,30 +27,12 @@ export class CommonExpressionHelpers {
this.patient = patient;
}

isEmpty = value => {
if (this.allFieldsKeys.includes(value)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == value),
);
return isValueEmpty(this.allFieldValues[value]);
}
return isValueEmpty(value);
};

today() {
return new Date();
}

includes = (questionId, value) => {
if (this.allFieldsKeys.includes(questionId)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id === questionId),
);
return this.allFieldValues[questionId]?.includes(value);
}
return false;
includes = (collection: any[], value: any) => {
return collection?.includes(value);
};

isDateBefore = (left: Date, right: string | Date, format?: string) => {
Expand Down Expand Up @@ -105,17 +87,7 @@ export class CommonExpressionHelpers {
return null;
};

calcBMI = (heightQuestionId, weightQuestionId) => {
const height = this.allFieldValues[heightQuestionId];
const weight = this.allFieldValues[weightQuestionId];
[heightQuestionId, weightQuestionId].forEach(entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
});
calcBMI = (height, weight) => {
let r;
if (height && weight) {
r = (weight / (((height / 100) * height) / 100)).toFixed(1);
Expand All @@ -128,52 +100,25 @@ export class CommonExpressionHelpers {
* @param lmpQuestionId
* @returns
*/
calcEDD = lmpQuestionId => {
const lmp = this.allFieldValues[lmpQuestionId];
[lmpQuestionId].forEach(entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
});
calcEDD = lmp => {
let resultEdd = {};
if (lmp) {
resultEdd = new Date(lmp.getTime() + 280 * 24 * 60 * 60 * 1000);
}
return lmp ? resultEdd : null;
};

calcMonthsOnART = artStartDateQuestionId => {
calcMonthsOnART = artStartDate => {
let today = new Date();
const artStartDate = this.allFieldValues[artStartDateQuestionId] || today;
[artStartDateQuestionId].forEach(entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
});
let resultMonthsOnART;
let artInDays = Math.round((today.getTime() - artStartDate.getTime()) / 86400000);
let artInDays = Math.round((today.getTime() - artStartDate.getTime?.()) / 86400000);
if (artStartDate && artInDays >= 30) {
resultMonthsOnART = Math.floor(artInDays / 30);
}
return artStartDate ? resultMonthsOnART : null;
};

calcViralLoadStatus = viralLoadCountQuestionId => {
const viralLoadCount = this.allFieldValues[viralLoadCountQuestionId];
[viralLoadCountQuestionId].forEach(entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
});
calcViralLoadStatus = viralLoadCount => {
let resultViralLoadStatus;
if (viralLoadCount) {
if (viralLoadCount > 50) {
Expand All @@ -185,43 +130,15 @@ export class CommonExpressionHelpers {
return viralLoadCount ? resultViralLoadStatus : null;
};

calcNextVisitDate = (followupDateQuestionId, arvDispensedInDaysQuestionId) => {
const followupDate = this.allFieldValues[followupDateQuestionId];
const arvDispensedInDays = this.allFieldValues[arvDispensedInDaysQuestionId];
[followupDateQuestionId, arvDispensedInDaysQuestionId].forEach(entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
});
calcNextVisitDate = (followupDate, arvDispensedInDays) => {
let resultNextVisitDate = {};
if (followupDate && arvDispensedInDays) {
resultNextVisitDate = new Date(followupDate.getTime() + arvDispensedInDays * 24 * 60 * 60 * 1000);
}
return followupDate && arvDispensedInDays ? resultNextVisitDate : null;
};

calcTreatmentEndDate = (
followupDateQuestionId,
arvDispensedInDaysQuestionId,
patientStatusQuestionId,
treatmentEndDateQuestionId,
) => {
const followupDate = this.allFieldValues[followupDateQuestionId];
const arvDispensedInDays = this.allFieldValues[arvDispensedInDaysQuestionId];
const patientStatus = this.allFieldValues[patientStatusQuestionId];
[followupDateQuestionId, arvDispensedInDaysQuestionId, patientStatusQuestionId, treatmentEndDateQuestionId].forEach(
entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
},
);
calcTreatmentEndDate = (followupDate, arvDispensedInDays, patientStatus) => {
let resultTreatmentEndDate = {};
let extraDaysAdded = 30 + arvDispensedInDays;
if (followupDate && arvDispensedInDays && patientStatus == '160429AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') {
Expand All @@ -232,39 +149,20 @@ export class CommonExpressionHelpers {
: null;
};

calcAgeBasedOnDate = questionId => {
const value = this.allFieldValues[questionId];
[questionId].forEach(entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
});
calcAgeBasedOnDate = dateValue => {
let targetYear = null;
if (value) {
targetYear = new Date(value).getFullYear();
if (dateValue) {
targetYear = new Date(dateValue).getFullYear();
} else {
targetYear = new Date().getFullYear();
}
let birthDate = new Date(this.patient.birthDate).getFullYear();
let calculatedYear = targetYear - birthDate;
return calculatedYear;
};

//Ampath Helper Functions
calcBSA = (heightQuestionId, weightQuestionId) => {
const height = this.allFieldValues[heightQuestionId];
const weight = this.allFieldValues[weightQuestionId];

[heightQuestionId, weightQuestionId].forEach(entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
});
calcBSA = (height, weight) => {
let result;
if (height && weight) {
result = Math.sqrt((height * weight) / 3600).toFixed(2);
Expand Down Expand Up @@ -351,17 +249,8 @@ export class CommonExpressionHelpers {
return daySinceLastCircumcision;
}

calcTimeDifference = (obsDateId, timeFrame) => {
calcTimeDifference = (obsDate, timeFrame) => {
let daySinceLastObs;
let obsDate = this.allFieldValues[obsDateId];
[obsDateId].forEach(entry => {
if (this.allFieldsKeys.includes(entry)) {
registerDependency(
this.node,
this.allFields.find(candidate => candidate.id == entry),
);
}
});
const endDate = moment(new Date());
const duration = moment.duration(endDate.diff(obsDate));

Expand All @@ -384,6 +273,9 @@ export class CommonExpressionHelpers {
}

export function registerDependency(node: FormNode, determinant: OHRIFormField) {
if (!node || !determinant) {
return;
}
switch (node.type) {
case 'page':
if (!determinant.pageDependants) {
Expand Down
Loading

0 comments on commit e9a87ae

Please sign in to comment.