Skip to content

Commit

Permalink
fix: antimicrobial rules + TEA program rules enabled
Browse files Browse the repository at this point in the history
  • Loading branch information
9sneha-n committed Aug 26, 2024
1 parent 1facbcc commit bcfb3ac
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 29 deletions.
6 changes: 6 additions & 0 deletions src/data/entities/D2Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export interface D2ProgramRuleVariable {
dataElement: {
id: Id;
};
trackedEntityAttribute?: {
id: Id;
};
}

export interface D2ProgramRuleAction {
Expand All @@ -116,6 +119,9 @@ export interface D2ProgramRuleAction {
dataElement?: {
id: Id | undefined;
};
trackedEntityAttribute: {
id: Id | undefined;
};
data?: string;
programStageSection?: {
id: Id | undefined;
Expand Down
47 changes: 39 additions & 8 deletions src/data/utils/ruleHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ export const getProgramRules = (
): QuestionnaireRule[] => {
return (
programRulesResponse?.map(({ id, condition, programRuleActions: actions }) => {
const dataElementVariablePattern = /#{(.*?)}/g;
const attributeVariablePattern = /A{(.*?)}/g;
const dataElementIds =
condition.match(/#{(.*?)}/g)?.map(programRuleVariableName => {
condition.match(dataElementVariablePattern)?.map(programRuleVariableName => {
const variableName = programRuleVariableName.replace(/#{|}/g, "");

const dataElementId = programRuleVariables?.find(
Expand All @@ -23,14 +25,41 @@ export const getProgramRules = (
}
return dataElementId;
}) || [];
const teaIds =
condition.match(attributeVariablePattern)?.map(programRuleVariableName => {
const variableName = programRuleVariableName.replace(/A{|}/g, "");

const parsedCondition = condition.replace(/#{(.*?)}/g, (match, programRuleVar) => {
const dataElementId = programRuleVariables?.find(
programRuleVariable => programRuleVariable.name === programRuleVar
)?.dataElement?.id;
const attributeId = programRuleVariables?.find(
programRuleVariable => variableName === programRuleVariable.name
)?.trackedEntityAttribute?.id;

if (!attributeId) {
console.debug(`Could not find attributeId for variable: ${variableName}`);
}
return attributeId;
}) || [];

const parsedCondition = condition.replace(
dataElementVariablePattern,
(match, programRuleVar) => {
const dataElementId = programRuleVariables?.find(
programRuleVariable => programRuleVariable.name === programRuleVar
)?.dataElement?.id;

return `#{${dataElementId}}`;
}
);

const attributeParsedCondition = parsedCondition.replace(
attributeVariablePattern,
(match, programRuleVar) => {
const attributeId = programRuleVariables?.find(
programRuleVariable => programRuleVariable.name === programRuleVar
)?.trackedEntityAttribute?.id;

return `#{${dataElementId}}`;
});
return `#{${attributeId}}`;
}
);

const programRuleActionIds: string[] = actions.map(action => action.id);

Expand All @@ -42,6 +71,7 @@ export const getProgramRules = (
programRuleActionType: programRuleAction.programRuleActionType,
data: programRuleAction.data,
dataElement: programRuleAction.dataElement,
trackedEntityAttribute: programRuleAction.trackedEntityAttribute,
programStageSection: {
id: programRuleAction.programStageSection?.id,
},
Expand All @@ -54,8 +84,9 @@ export const getProgramRules = (

return {
id: id,
condition: parsedCondition.replace(/d2:/g, "fn:"), //replace d2: with fn: to decouple entity from DHIS
condition: attributeParsedCondition.replace(/d2:/g, "fn:"), //replace d2: with fn: to decouple entity from DHIS
dataElementIds: _(dataElementIds).uniq().compact().value(),
teAttributeIds: _(teaIds).uniq().compact().value(),
actions: programRuleActions || [],
};
}) || []
Expand Down
51 changes: 39 additions & 12 deletions src/domain/entities/Questionnaire/Questionnaire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { generateUid } from "../../../utils/uid";
import { SurveyRule } from "../AMRSurveyModule";
import { Id, Ref } from "../Ref";
import _ from "../generic/Collection";
import { Code, Question, isAntibioticQuestion } from "./QuestionnaireQuestion";
import {
Code,
Question,
QuestionnaireQuestion,
isAntibioticQuestion,
} from "./QuestionnaireQuestion";
import { QuestionnaireRule, getApplicableRules } from "./QuestionnaireRules";
import { QuestionnaireSection, QuestionnaireSectionM } from "./QuestionnaireSection";

Expand Down Expand Up @@ -142,14 +147,19 @@ export class Questionnaire {
try {
if (!questionnaire.rules || questionnaire.rules.length === 0) return questionnaire;

const allQsInQuestionnaire: Question[] = questionnaire.stages.flatMap(stage => {
const allQsInQuestionnaireStages: Question[] = questionnaire.stages.flatMap(stage => {
return stage.sections.flatMap(section => {
return section.questions.map(question => {
return { ...question, stageId: stage.id };
});
});
});

const allQsInQuestionnaire = [
...(questionnaire.entity?.questions || []),
...allQsInQuestionnaireStages,
];

const updatedQuestionnaire = allQsInQuestionnaire.reduce(
(questionnaireAcc, question) => {
return this.updateQuestionnaire(
Expand Down Expand Up @@ -249,11 +259,18 @@ export class Questionnaire {
initialLoad = false
): Questionnaire {
//For the updated question, get all rules that are applicable
const allQsInQuestionnaire = questionnaire.stages.flatMap((stage: QuestionnaireStage) => {
return stage.sections.flatMap(section => {
return section.questions.map(question => question);
});
});
const allQsInQuestionnaireStages = questionnaire.stages.flatMap(
(stage: QuestionnaireStage) => {
return stage.sections.flatMap(section => {
return section.questions.map(question => question);
});
}
);

const allQsInQuestionnaire = [
...(questionnaire.entity?.questions || []),
...allQsInQuestionnaireStages,
];

const applicableRules = getApplicableRules(
updatedQuestion,
Expand Down Expand Up @@ -283,7 +300,12 @@ export class Questionnaire {
}),
entity:
isEntityQuestionUpdated && questionnaire.entity
? this.updateEntityQuestion(questionnaire.entity, updatedQuestion)
? this.updateEntityQuestion(
questionnaire.entity,
updatedQuestion,
questionnaire,
applicableRules
)
: questionnaire.entity,
});
}
Expand Down Expand Up @@ -343,11 +365,16 @@ export class Questionnaire {

static updateEntityQuestion(
questionnaireEntity: QuestionnaireEntity,
updatedQuestion: Question
updatedQuestion: Question,
questionnaire: Questionnaire,
rules: QuestionnaireRule[]
): QuestionnaireEntity | undefined {
const updatedEntityQuestions = questionnaireEntity.questions.map(question => {
return question.id === updatedQuestion.id ? updatedQuestion : question;
});
const updatedEntityQuestions = QuestionnaireQuestion.updateQuestions(
questionnaireEntity.questions,
updatedQuestion,
rules,
questionnaire
);

return {
...questionnaireEntity,
Expand Down
33 changes: 26 additions & 7 deletions src/domain/entities/Questionnaire/QuestionnaireQuestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,21 +155,38 @@ export class QuestionnaireQuestion {
questions: Question[],
updatedQuestion: Question,
rules: QuestionnaireRule[],
questionnaire: Questionnaire
questionnaire: Questionnaire,
parentSectionHidden?: boolean
): Question[] {
//1. Update the question value before anything else, the updated value needs to be used to parse rule conditions
const updatedQuestions = questions.map(question => {
return question.id === updatedQuestion.id ? updatedQuestion : question;
if (question.id === updatedQuestion.id) {
return updatedQuestion;
} else if (
parentSectionHidden &&
question.text.includes("Add another") &&
question.type === "boolean"
) {
return {
...question,
value: false,
};
} else {
return question;
}
});

//2. Now, apply all possible side effects of the updated value to the rest of the questionnaire.

//Get list question ids that require update
const allQuestionsRequiringUpdate = _(
rules.flatMap(rule => {
const actionUpdates = rule.actions.flatMap(action => action?.dataElement?.id);
const actionUpdates = rule.actions.flatMap(
action => action?.dataElement?.id || action.trackedEntityAttribute?.id
);
const dataElementUpdates = rule.dataElementIds;
return [...actionUpdates, ...dataElementUpdates];
const teaUpdates = rule.teAttributeIds;
return [...actionUpdates, ...dataElementUpdates, ...teaUpdates];
})
)
.compact()
Expand Down Expand Up @@ -238,9 +255,11 @@ export class QuestionnaireQuestion {
rule =>
rule.actions.filter(
action =>
action.programRuleActionType === "HIDEFIELD" &&
action.dataElement &&
action.dataElement.id === question.id
(action.programRuleActionType === "HIDEFIELD" &&
action.dataElement &&
action.dataElement.id === question.id) ||
(action.trackedEntityAttribute &&
action.trackedEntityAttribute.id === question.id)
).length > 0
);
if (!applicableRules || applicableRules.length === 0) return question.isVisible;
Expand Down
8 changes: 7 additions & 1 deletion src/domain/entities/Questionnaire/QuestionnaireRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export interface QuestionnaireRuleAction {
dataElement?: {
id: Id | undefined; // to hide
};
trackedEntityAttribute?: {
id: Id | undefined; // to hide
};
data?: string; // to assign
programStageSection?: {
id: Id | undefined; // to hide/show
Expand All @@ -44,6 +47,7 @@ export interface QuestionnaireRule {
id: Id;
condition: string; //condition is parsed with dataelementId e.g: #{dataElementId} == 'Yes'
dataElementIds: Id[]; // all dataElements in condition (there could be mutiple conditions)
teAttributeIds: Id[]; // all trackedEntityAttributes in condition (there could be mutiple conditions)
actions: QuestionnaireRuleAction[];
parsedResult?: boolean; //calculate the condition and store the result
}
Expand All @@ -57,7 +61,9 @@ export const getApplicableRules = (
const applicableRules = questionnaireRules.filter(
rule =>
rule.dataElementIds.includes(updatedQuestion.id) ||
rule.actions.some(action => action.dataElement?.id === updatedQuestion.id)
rule.teAttributeIds.includes(updatedQuestion.id) ||
rule.actions.some(action => action.dataElement?.id === updatedQuestion.id) ||
rule.actions.some(action => action.trackedEntityAttribute?.id === updatedQuestion.id)
);

//2. Run the rule conditions and return rules with parsed results
Expand Down
3 changes: 2 additions & 1 deletion src/domain/entities/Questionnaire/QuestionnaireSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ export class QuestionnaireSectionM {
updatedSection.questions,
updatedQuestion,
rules,
questionnaire
questionnaire,
updatedSection.isVisible === false && section.isVisible === true
),
};
});
Expand Down

0 comments on commit bcfb3ac

Please sign in to comment.