All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hisp.dhis.rules.RuleConditionEvaluator Maven / Gradle / Ivy

package org.hisp.dhis.rules;

import org.apache.commons.lang3.StringUtils;
import org.hisp.dhis.antlr.Parser;
import org.hisp.dhis.antlr.ParserExceptionWithoutContext;
import org.hisp.dhis.rules.models.*;
import org.hisp.dhis.rules.parser.expression.CommonExpressionVisitor;
import org.hisp.dhis.rules.utils.RuleEngineUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.*;

import static org.hisp.dhis.rules.parser.expression.ParserUtils.FUNCTION_EVALUATE;

public class RuleConditionEvaluator
{
    private static final Logger log = LoggerFactory.getLogger( RuleConditionEvaluator.class.getName() );

    public List getEvaluatedAndErrorRuleEffects( TrackerObjectType targetType, String targetUid, Map valueMap,
                                            Map> supplementaryData, List rules )
    {
        List ruleEffects = new ArrayList<>();
        for (RuleEvaluationResult ruleEvaluationResult : getRuleEvaluationResults( targetType, targetUid, valueMap, supplementaryData, rules)) {
                ruleEffects.addAll( ruleEvaluationResult.getRuleEffects() );
        }

        return ruleEffects;
    }

    public List getRuleEffects( TrackerObjectType targetType, String targetUid, Map valueMap,
                                            Map> supplementaryData, List rules )
    {
        List ruleEffects = new ArrayList<>();
        for (RuleEvaluationResult ruleEvaluationResult : getRuleEvaluationResults( targetType, targetUid, valueMap, supplementaryData, rules)) {

            if ( !ruleEvaluationResult.isError() ) {
                ruleEffects.addAll( ruleEvaluationResult.getRuleEffects() );
            }
        }

        return ruleEffects;
    }

    public List getRuleEvaluationResults( TrackerObjectType targetType, String targetUid,
                                                    Map valueMap,
                                                    Map> supplementaryData, List rules )
    {
        List ruleEvaluationResults = new ArrayList<>();

        rules = orderRules( rules );
        valueMap = new HashMap<>( valueMap );

        for ( Rule rule : orderRules( rules ) )
        {
            log.debug( "Evaluating programrule: " + rule.name() );

            try {
                List ruleEffects = new ArrayList<>();

            if ( Boolean.parseBoolean( process( rule.condition(), valueMap, supplementaryData ) ) )
            {
                for ( RuleAction action : rule.actions() )
                {

                        try {
                            //Check if action is assigning value to calculated variable
                            if ( isAssignToCalculatedValue( action ) )
                            {
                                RuleActionAssign ruleActionAssign = (RuleActionAssign) action;
                                updateValueMap(
                                        Utils.unwrapVariableName(ruleActionAssign.content()),
                                        RuleVariableValue.create(process( ruleActionAssign.data(), valueMap, supplementaryData),
                                                RuleValueType.TEXT),
                                        valueMap
                                );
                            }
                            else
                            {
                                ruleEffects.add( create( rule, action, valueMap, supplementaryData ) );
                            }
                        } catch ( Exception e ) {
                            addRuleErrorResult( rule,action, e, targetType, targetUid, ruleEvaluationResults );
                        }
                    }

                    ruleEvaluationResults.add(RuleEvaluationResult.evaluatedResult(rule, ruleEffects));
                } else {
                    ruleEvaluationResults.add(RuleEvaluationResult.notEvaluatedResult(rule));
                }
            } catch ( Exception e ) {
                addRuleErrorResult(rule, null, e, targetType, targetUid, ruleEvaluationResults);
            }
        }

        for (RuleEvaluationResult ruleEvaluationResult : ruleEvaluationResults) {

            log.debug("Rule " + ruleEvaluationResult.getRule().name() + " with id " + ruleEvaluationResult.getRule().uid() +
                    " executed for " + targetType.getName() + "(" + targetUid + ")" +
                    " with condition (" + ruleEvaluationResult.getRule().condition() + ")" +
                    " was evaluated " + ruleEvaluationResult.isEvaluatedAs());
        }

        return ruleEvaluationResults;

    }

    private void addRuleErrorResult( Rule rule, RuleAction ruleAction, Exception e, TrackerObjectType targetType,
                                    String targetUid, List ruleEvaluationResults )
    {
        String errorMessage;
        if ( ruleAction != null && e instanceof ParserExceptionWithoutContext )
        {
            errorMessage = "Action " + ruleAction.getClass().getName() +
                    " from rule " + rule.name() + " with id " + rule.uid() +
                    " executed for " + targetType.getName() + "(" + targetUid + ")" +
                    " with condition (" + rule.condition() + ")" +
                    " raised an error: " + e.getMessage();
        }
        else if ( ruleAction != null )
        {
            errorMessage = "Action " + ruleAction.getClass().getName() +
                    " from rule " + rule.name() + " with id " + rule.uid() +
                    " executed for " + targetType.getName() + "(" + targetUid + ")" +
                    " with condition (" + rule.condition() + ")" +
                    " raised an unexpected exception: " + e.getMessage();
        }
        else if(e instanceof ParserExceptionWithoutContext)
        {
            errorMessage = "Rule " + rule.name() + " with id " + rule.uid() +
                    " executed for " + targetType.getName() + "(" + targetUid + ")" +
                    " with condition (" + rule.condition() + ")" +
                    " raised an error: " + e.getMessage();
        }
        else
        {
            errorMessage = "Rule " + rule.name() + " with id " + rule.uid() +
                    " executed for " + targetType.getName() + "(" + targetUid + ")" +
                    " with condition (" + rule.condition() + ")" +
                    " raised an unexpected exception: " + e.getMessage();
        }

        log.error(errorMessage);
        ruleEvaluationResults.add(RuleEvaluationResult.errorRule(rule, errorMessage));
    }

    private List orderRules( List rules )
    {
        List ruleList = new ArrayList<>( rules );

        Collections.sort( ruleList, new Comparator()
        {
            @Override
            public int compare( Rule rule1, Rule rule2 )
            {
                Integer priority1 = rule1.priority();
                Integer priority2 = rule2.priority();
                if ( priority1 != null && priority2 != null )
                {
                    return priority1.compareTo( priority2 );
                }
                else if ( priority1 != null )
                {
                    return -1;
                }
                else if ( priority2 != null )
                {
                    return 1;
                }
                else
                {
                    return 0;
                }
            }
        } );

        return ruleList;
    }

    private String process( String condition, Map valueMap,
                            Map> supplementaryData )
    {
        if ( condition.isEmpty() )
        {
            return "";
        }

        CommonExpressionVisitor commonExpressionVisitor = CommonExpressionVisitor.newBuilder()
            .withFunctionMap( RuleEngineUtils.FUNCTIONS )
            .withFunctionMethod( FUNCTION_EVALUATE )
            .withVariablesMap( valueMap )
            .withSupplementaryData( supplementaryData )
            .validateCommonProperties();

        Object result = Parser.visit( condition, commonExpressionVisitor, !isOldAndroidVersion( valueMap, supplementaryData ) );
        return convertInteger( result ).toString();
    }

    private Object convertInteger( Object result )
    {
        if ( result instanceof Double && (Double) result % 1 == 0 )
        {
            return ((Double) result).intValue();
        }
        return result;
    }

    private Boolean isOldAndroidVersion( Map valueMap, Map> supplementaryData )
    {
        return valueMap.containsKey( "environment" ) &&
            Objects.equals( valueMap.get( "environment" ).value(), TriggerEnvironment.ANDROIDCLIENT.getClientName() ) &&
            supplementaryData.containsKey( "android_version" ) &&
            Integer.parseInt( supplementaryData.get( "android_version" ).get( 0 ) ) < 21;
    }

    private Boolean isAssignToCalculatedValue( RuleAction ruleAction )
    {
        return ruleAction instanceof RuleActionAssign && ((RuleActionAssign) ruleAction).field().isEmpty();
    }

    private void updateValueMap( String variable, RuleVariableValue value, Map valueMap )
    {
        valueMap.put( variable, value );
    }

    @Nonnull
    private RuleEffect create( @Nonnull Rule rule,
                                @Nonnull RuleAction ruleAction,
                                Map valueMap,
                                Map> supplementaryData )
    {
        if ( ruleAction instanceof RuleActionAssign )
        {
            RuleActionAssign ruleActionAssign = (RuleActionAssign) ruleAction;
            String data = process( ruleActionAssign.data(), valueMap, supplementaryData );
            updateValueMap( ruleActionAssign.field(), RuleVariableValue.create( data, RuleValueType.TEXT ), valueMap );
            if ( StringUtils.isEmpty( data ) && StringUtils.isEmpty( ruleActionAssign.data() ) )
            {
                return RuleEffect.create( rule.uid(), ruleAction, ruleActionAssign.data() );
            }
            else
            {
                return RuleEffect.create( rule.uid(), ruleAction, data );
            }
        }

        return RuleEffect.create( rule.uid(), ruleAction, process( ruleAction.data(), valueMap, supplementaryData ) );
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy