diff --git a/src/data/utils/ruleHelper.ts b/src/data/utils/ruleHelper.ts index 5dba41e..09ea173 100644 --- a/src/data/utils/ruleHelper.ts +++ b/src/data/utils/ruleHelper.ts @@ -39,28 +39,6 @@ export const getProgramRules = ( 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 `#{${attributeId}}`; - } - ); - const programRuleActionIds: string[] = actions.map(action => action.id); const programRuleActions: D2ProgramRuleAction[] | undefined = programRuleActionsResponse @@ -84,8 +62,6 @@ export const getProgramRules = ( return { id: id, - condition: attributeParsedCondition.replace(/d2:/g, "fn:"), //replace d2: with fn: to decouple entity from DHIS - d2Condition: attributeParsedCondition, originalCondition: condition, dataElementIds: _(dataElementIds).uniq().compact().value(), teAttributeIds: _(teaIds).uniq().compact().value(), diff --git a/src/domain/entities/Questionnaire/QuestionnaireRules.ts b/src/domain/entities/Questionnaire/QuestionnaireRules.ts index 25d79d4..3c843cc 100644 --- a/src/domain/entities/Questionnaire/QuestionnaireRules.ts +++ b/src/domain/entities/Questionnaire/QuestionnaireRules.ts @@ -9,16 +9,6 @@ import { Id } from "../Ref"; import _ from "../generic/Collection"; import { Question } from "./QuestionnaireQuestion"; -const RULE_FUNCTIONS = ["fn:hasValue", "fn:daysBetween", "fn:yearsBetween"]; -const RULE_OPERATORS = [ - ">" as const, - ">=" as const, - "<" as const, - "<=" as const, - "==" as const, - "!=" as const, -]; - export type QuestionnaireRuleActionType = | "DISPLAYTEXT" | "DISPLAYKEYVALUEPAIR" @@ -52,8 +42,8 @@ export interface QuestionnaireRuleAction { } export interface QuestionnaireRule { id: Id; - condition: string; //condition is parsed with dataelementId e.g: #{dataElementId} == 'Yes' - d2Condition: string; //SNEHA TO DO : remove above condition and use this condition after testing + // condition: string; //condition is parsed with dataelementId e.g: #{dataElementId} == 'Yes' + // d2Condition: string; //SNEHA TO DO : remove above condition and use this condition after testing originalCondition: string; dataElementIds: Id[]; // all dataElements in condition (there could be mutiple conditions) teAttributeIds: Id[]; // all trackedEntityAttributes in condition (there could be mutiple conditions) @@ -78,23 +68,8 @@ export const getApplicableRules = ( //2. Run the rule conditions and return rules with parsed results const parsedApplicableRules = applicableRules.map(rule => { - const customParserResult = parseCondition(rule.condition, updatedQuestion, questions); - const expressionParserResult = parseConditionWithExpressionParser(rule, questions); - //SNEHA DEBUG - if (customParserResult !== expressionParserResult) { - console.debug( - `custom parser and expression parser give diffrent results for rule : ${ - rule.id - }, condition : ${ - rule.originalCondition - }, custom parser : ${expressionParserResult}, expression parser : ${customParserResult}, value: ${ - questions.find(q => q.id === rule.dataElementIds[0])?.value - }` - ); - } - return { ...rule, parsedResult: expressionParserResult }; }); @@ -120,183 +95,6 @@ export const getQuestionValueByType = (question: Question): string => { } }; -const parseConditionValues = ( - condition: string, - updatedQuestion: Question, - questions: Question[] -) => { - return condition.replace(/#\{(.*?)\}/g, (_i, dataElementId) => { - const isDataElementInUpdatedQuestion = updatedQuestion.id === dataElementId; - if (isDataElementInUpdatedQuestion) { - return getQuestionValueByType(updatedQuestion); - } else { - const currentQuestion = questions.find( - (question: Question) => question.id === dataElementId - ); - return currentQuestion ? getQuestionValueByType(currentQuestion) : ""; - } - }); -}; - -const handleRuleFunctions = (condition: string): boolean => { - const ruleFunction = RULE_FUNCTIONS.find(rulefunc => condition.includes(rulefunc)); - - switch (ruleFunction) { - case "fn:hasValue": { - const match = condition.match(/fn:hasValue\((.*?)\)/); - if (match) { - const innerString = match[1]; - if (innerString?.trim() === "") { - return false; - } else { - return true; - } - } else return false; - } - - default: - console.debug(`Unkown rule function: ${ruleFunction}`); - return false; - } -}; - -const handleCondition = (condition: string): boolean => { - const operator = RULE_OPERATORS.find(ruleOperator => condition.includes(ruleOperator)); - - if (!operator || !RULE_OPERATORS.includes(operator)) - throw new Error(`Operator ${operator} is either undefined or not handled`); - - const leftOperand = condition - .substring(0, condition.indexOf(operator)) - .replaceAll("'", "") - .trim(); - - const rightOperandStr = condition - .substring(condition.indexOf(operator)) - .replace(operator, "") - .replaceAll("'", "") - .trim(); - - // Handle right operands boolean values of "1" and "0" for true and false - const rightOperand = - leftOperand === "true" && rightOperandStr === "1" - ? "true" - : leftOperand === "false" && rightOperandStr === "0" - ? "false" - : rightOperandStr; - - switch (operator) { - case "!=": { - return leftOperand !== rightOperand; - } - case "==": { - return leftOperand === rightOperand; - } - case ">": { - try { - return parseFloat(leftOperand) > parseFloat(rightOperand); - } catch { - return false; - } - } - case ">=": { - try { - return parseFloat(leftOperand) >= parseFloat(rightOperand); - } catch { - return false; - } - } - case "<": { - try { - return parseFloat(leftOperand) < parseFloat(rightOperand); - } catch { - return false; - } - } - case "<=": { - try { - return parseFloat(leftOperand) <= parseFloat(rightOperand); - } catch { - return false; - } - } - default: - throw new Error(`Operator ${operator} not handled`); - } -}; - -const parseAndEvaluateSubCondition = ( - subCondition: string, - updatedQuestion: Question, - questions: Question[] -): boolean => { - // Replace #{dataElementId} with actual value from questionnaire - const parsedConditionWithValues = parseConditionValues( - subCondition, - updatedQuestion, - questions - ); - - // Evaluate the condition - try { - if (RULE_FUNCTIONS.some(ruleFunction => parsedConditionWithValues.includes(ruleFunction))) { - return handleRuleFunctions(parsedConditionWithValues); - } else { - return handleCondition(parsedConditionWithValues); - } - } catch (error) { - console.error( - `Error evaluating condition: ${parsedConditionWithValues} with error : ${error}` - ); - return false; - } -}; - -const parseCondition = ( - condition: string, - updatedQuestion: Question, - questions: Question[] -): boolean => { - // Create a regular expression from RULE_FUNCTIONS array - const ruleFunctionsRegex = new RegExp(`(? { - return parseCondition(subCondition, updatedQuestion, questions) - ? "true" - : "false"; - } - ) - : condition; - - // Split condition into sub-conditions based on logical operators - const andConditions = newCondition.split("&&").map(subCondition1 => { - const orConditions = subCondition1.split("||").map(subCondition2 => { - const notCondition = subCondition2.trim().replace("(", "").startsWith("!"); - const trimmedSubCondition = notCondition - ? subCondition2.trim().substring(1) - : subCondition2; - - const result = - trimmedSubCondition.replace(/\s/g, "") === "true" - ? true - : trimmedSubCondition.replace(/\s/g, "") === "false" - ? false - : parseAndEvaluateSubCondition(trimmedSubCondition, updatedQuestion, questions); - - return notCondition ? !result : result; - }); - - return orConditions.some(condition => condition); - }); - - return andConditions.every(condition => condition); -}; - function getProgramRuleVariableValues( programRuleVariables: Maybe, questions: Question[] @@ -341,7 +139,9 @@ const parseConditionWithExpressionParser = (rule: QuestionnaireRule, questions: programRuleVariableValues ); } catch (error) { - console.error(`Error parsing rule condition: ${rule.condition} with error : ${error}`); + console.error( + `Error parsing rule condition: ${rule.originalCondition} with error : ${error}` + ); return false; } };