|
| 1 | +package org.hisp.dhis.rules.engine |
| 2 | + |
| 3 | +import org.hisp.dhis.lib.expression.Expression |
| 4 | +import org.hisp.dhis.rules.api.RuleContextRequirements |
| 5 | +import org.hisp.dhis.rules.models.Rule |
| 6 | +import org.hisp.dhis.rules.models.RuleVariable |
| 7 | +import org.hisp.dhis.rules.models.RuleVariableAttribute |
| 8 | +import org.hisp.dhis.rules.models.RuleVariableCalculatedValue |
| 9 | +import org.hisp.dhis.rules.models.RuleVariableCurrentEvent |
| 10 | +import org.hisp.dhis.rules.models.RuleVariableNewestEvent |
| 11 | +import org.hisp.dhis.rules.models.RuleVariableNewestStageEvent |
| 12 | +import org.hisp.dhis.rules.models.RuleVariablePreviousEvent |
| 13 | +import org.hisp.dhis.rules.utils.RuleEngineUtils.ENV_VAR_ENROLLMENT_COUNT |
| 14 | +import org.hisp.dhis.rules.utils.RuleEngineUtils.ENV_VAR_ENROLLMENT_DATE |
| 15 | +import org.hisp.dhis.rules.utils.RuleEngineUtils.ENV_VAR_ENROLLMENT_ID |
| 16 | +import org.hisp.dhis.rules.utils.RuleEngineUtils.ENV_VAR_ENROLLMENT_STATUS |
| 17 | +import org.hisp.dhis.rules.utils.RuleEngineUtils.ENV_VAR_EVENT_COUNT |
| 18 | +import org.hisp.dhis.rules.utils.RuleEngineUtils.ENV_VAR_TEI_COUNT |
| 19 | + |
| 20 | +internal object RuleEngineAnalyzer { |
| 21 | + private val MULTI_EVENT_ENV_VARS = setOf(ENV_VAR_EVENT_COUNT) |
| 22 | + private val ENROLLMENT_ENV_VARS = setOf( |
| 23 | + ENV_VAR_ENROLLMENT_COUNT, |
| 24 | + ENV_VAR_ENROLLMENT_DATE, |
| 25 | + ENV_VAR_ENROLLMENT_ID, |
| 26 | + ENV_VAR_ENROLLMENT_STATUS, |
| 27 | + ENV_VAR_TEI_COUNT, |
| 28 | + ) |
| 29 | + |
| 30 | + fun analyzeContextRequirements( |
| 31 | + rules: List<Rule>, |
| 32 | + variables: List<RuleVariable>, |
| 33 | + ): RuleContextRequirements { |
| 34 | + val byName = variables.associateBy { it.name } |
| 35 | + val hasNonCalculatedVariables = variables.any { it !is RuleVariableCalculatedValue } |
| 36 | + val acc = RequirementsAccumulator(byName) |
| 37 | + |
| 38 | + for (rule in rules) { |
| 39 | + if (acc.isComplete) break |
| 40 | + for (expr in rule.expressions()) { |
| 41 | + acc.processEnvVars(expr) |
| 42 | + if (hasNonCalculatedVariables) acc.processVarNames(expr) |
| 43 | + acc.processOrgUnitGroups(expr) |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + return acc.toRequirements() |
| 48 | + } |
| 49 | + |
| 50 | + private fun Rule.expressions(): List<Expression> = buildList { |
| 51 | + conditionExpression.getOrNull()?.let { add(it) } |
| 52 | + actions.forEach { action -> action.dataExpression.getOrNull()?.let { add(it) } } |
| 53 | + } |
| 54 | + |
| 55 | + private class RequirementsAccumulator(private val byName: Map<String, RuleVariable>) { |
| 56 | + var needsAllEvents = false |
| 57 | + var needsEnrollment = false |
| 58 | + var needsDataValues = false |
| 59 | + var needsAttributes = false |
| 60 | + var needsOrgUnitGroups = false |
| 61 | + |
| 62 | + val isComplete get() = needsAllEvents && needsEnrollment && needsDataValues && needsAttributes && needsOrgUnitGroups |
| 63 | + |
| 64 | + fun processEnvVars(expr: Expression) { |
| 65 | + if (needsAllEvents && needsEnrollment) return |
| 66 | + for (envVar in expr.collectProgramVariablesNames()) { |
| 67 | + needsAllEvents = needsAllEvents || envVar in MULTI_EVENT_ENV_VARS |
| 68 | + needsEnrollment = needsEnrollment || envVar in ENROLLMENT_ENV_VARS |
| 69 | + if (needsAllEvents && needsEnrollment) break |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + fun processVarNames(expr: Expression) { |
| 74 | + if (needsAllEvents && needsDataValues && needsAttributes) return |
| 75 | + for (name in expr.collectProgramRuleVariableNames()) { |
| 76 | + val v = byName[name] |
| 77 | + val isMultiEvent = v is RuleVariableNewestEvent || v is RuleVariableNewestStageEvent || v is RuleVariablePreviousEvent |
| 78 | + needsAllEvents = needsAllEvents || isMultiEvent |
| 79 | + needsDataValues = needsDataValues || isMultiEvent || v is RuleVariableCurrentEvent |
| 80 | + needsAttributes = needsAttributes || v is RuleVariableAttribute |
| 81 | + needsEnrollment = needsEnrollment || needsAttributes |
| 82 | + if (needsAllEvents && needsDataValues && needsAttributes) break |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + fun processOrgUnitGroups(expr: Expression) { |
| 87 | + if (!needsOrgUnitGroups) needsOrgUnitGroups = expr.collectInOrgUnitGroups().isNotEmpty() |
| 88 | + } |
| 89 | + |
| 90 | + fun toRequirements() = RuleContextRequirements( |
| 91 | + needsAllEvents, needsEnrollment, needsDataValues, needsAttributes, needsOrgUnitGroups, |
| 92 | + ) |
| 93 | + } |
| 94 | +} |
0 commit comments