=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/expression/ExpressionService.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/expression/ExpressionService.java 2011-05-20 12:27:47 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/expression/ExpressionService.java 2011-05-20 16:00:10 +0000 @@ -56,9 +56,10 @@ final String NULL_REPLACEMENT = "0"; final String SPACE = " "; - - final String FORMULA_EXPRESSION = "\\[\\d+?.*?\\]"; // [ - one or more digits - any characters - ] - final String DAYS_EXPRESSION = "\\[days\\]"; // [days] + + final String FORMULA_EXPRESSION = "\\[.+?\\]"; + final String OPERAND_EXPRESSION = "\\[\\d+?.*?\\]"; + final String DAYS_EXPRESSION = "[days]"; /** * Adds a new Expression to the database. @@ -106,12 +107,13 @@ * @param nullIfNoValues indicates whether null should be returned if no * DataValues are registered for a DataElement in the expression. * @param aggregated indicates whether aggregated or raw data should be - * used when evaluating the expression. + * used when evaluating the expression. * + * @param days The number to be substituted with the days expression in the formula. * @return The value of the given Expression, or null * if no values are registered for a given combination of * DataElement, Source, and Period. */ - Double getExpressionValue( Expression expression, Period period, OrganisationUnit source, boolean nullIfNoValues, boolean aggregate ); + Double getExpressionValue( Expression expression, Period period, OrganisationUnit source, boolean nullIfNoValues, boolean aggregate, Integer days ); /** * Returns all DataElements included in the given expression string. @@ -192,9 +194,10 @@ * DataValues are registered for a DataElement in the expression. * @param aggregated indicates whether aggregated or raw data should be * used when evaluating the expression. + * @param days The number to be substituted with the days expression in the formula. * @return A numerical expression. */ - String generateExpression( String expression, Period period, OrganisationUnit source, boolean nullIfNoValues, boolean aggregated ); + String generateExpression( String expression, Period period, OrganisationUnit source, boolean nullIfNoValues, boolean aggregated, Integer days ); /** * Generates an expression where the Operand identifiers, consisting of @@ -204,6 +207,7 @@ * * @param formula The formula to parse. * @param valueMap The map containing data element identifiers and aggregated value. + * @param days The number to be substituted with the days expression in the formula. */ - String generateExpression( String expression, Map valueMap ); + String generateExpression( String expression, Map valueMap, Integer days ); } === modified file 'dhis-2/dhis-services/dhis-service-aggregationengine-default/src/main/java/org/hisp/dhis/aggregation/impl/indicator/IndicatorAggregation.java' --- dhis-2/dhis-services/dhis-service-aggregationengine-default/src/main/java/org/hisp/dhis/aggregation/impl/indicator/IndicatorAggregation.java 2011-05-20 13:27:02 +0000 +++ dhis-2/dhis-services/dhis-service-aggregationengine-default/src/main/java/org/hisp/dhis/aggregation/impl/indicator/IndicatorAggregation.java 2011-05-20 16:00:10 +0000 @@ -29,6 +29,7 @@ import static org.hisp.dhis.system.util.MathUtils.INVALID; import static org.hisp.dhis.system.util.MathUtils.calculateExpression; +import static org.hisp.dhis.system.util.DateUtils.daysBetween; import java.util.Date; import java.util.HashMap; @@ -91,11 +92,13 @@ public Double getAggregatedIndicatorValue( Indicator indicator, Date startDate, Date endDate, OrganisationUnit organisationUnit ) { + int days = daysBetween( startDate, endDate ); + double numeratorValue = calculateExpression( generateExpression( indicator.getNumerator(), startDate, - endDate, organisationUnit ) ); + endDate, organisationUnit, days ) ); double denominatorValue = calculateExpression( generateExpression( indicator.getDenominator(), - startDate, endDate, organisationUnit ) ); + startDate, endDate, organisationUnit, days ) ); if ( numeratorValue == INVALID || denominatorValue == INVALID || denominatorValue == 0.0 ) { @@ -117,21 +120,21 @@ OrganisationUnit organisationUnit ) { return calculateExpression( generateExpression( indicator.getNumerator(), startDate, - endDate, organisationUnit ) ); + endDate, organisationUnit, daysBetween( startDate, endDate ) ) ); } public double getAggregatedDenominatorValue( Indicator indicator, Date startDate, Date endDate, OrganisationUnit organisationUnit ) { return calculateExpression( generateExpression( indicator.getDenominator(), - startDate, endDate, organisationUnit ) ); + startDate, endDate, organisationUnit, daysBetween( startDate, endDate ) ) ); } // ------------------------------------------------------------------------- // Supportive methods // ------------------------------------------------------------------------- - private String generateExpression( String expression, Date startDate, Date endDate, OrganisationUnit organisationUnit ) + private String generateExpression( String expression, Date startDate, Date endDate, OrganisationUnit organisationUnit, int days ) { Set operands = expressionService.getOperandsInExpression( expression ); @@ -145,6 +148,6 @@ valueMap.put( operand, aggregationCache.getAggregatedDataValue( dataElement, optionCombo, startDate, endDate, organisationUnit ) ); } - return expressionService.generateExpression( expression, valueMap ); + return expressionService.generateExpression( expression, valueMap, null ); } } === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/expression/DefaultExpressionService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/expression/DefaultExpressionService.java 2011-05-20 12:27:47 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/expression/DefaultExpressionService.java 2011-05-20 16:00:10 +0000 @@ -72,7 +72,9 @@ private static final Log log = LogFactory.getLog( DefaultExpressionService.class ); private final Pattern FORMULA_PATTERN = Pattern.compile( FORMULA_EXPRESSION ); - + private final Pattern OPERAND_PATTERN = Pattern.compile( OPERAND_EXPRESSION ); + private final String DAYS_DESCRIPTION = "[Number of days]"; + // ------------------------------------------------------------------------- // Dependencies // ------------------------------------------------------------------------- @@ -145,9 +147,9 @@ // Business logic // ------------------------------------------------------------------------- - public Double getExpressionValue( Expression expression, Period period, OrganisationUnit source, boolean nullIfNoValues, boolean aggregate ) + public Double getExpressionValue( Expression expression, Period period, OrganisationUnit source, boolean nullIfNoValues, boolean aggregate, Integer days ) { - final String expressionString = generateExpression( expression.getExpression(), period, source, nullIfNoValues, aggregate ); + final String expressionString = generateExpression( expression.getExpression(), period, source, nullIfNoValues, aggregate, days ); return expressionString != null ? calculateExpression( expressionString ) : null; } @@ -160,7 +162,7 @@ { dataElementsInExpression = new HashSet(); - final Matcher matcher = FORMULA_PATTERN.matcher( expression ); + final Matcher matcher = OPERAND_PATTERN.matcher( expression ); while ( matcher.find() ) { @@ -182,7 +184,7 @@ if ( expression != null ) { - final Matcher matcher = FORMULA_PATTERN.matcher( expression ); + final Matcher matcher = OPERAND_PATTERN.matcher( expression ); while ( matcher.find() ) { @@ -227,7 +229,7 @@ { operandsInExpression = new HashSet(); - final Matcher matcher = FORMULA_PATTERN.matcher( expression ); + final Matcher matcher = OPERAND_PATTERN.matcher( expression ); while ( matcher.find() ) { @@ -253,25 +255,30 @@ { DataElementOperand operand = null; - try - { - operand = DataElementOperand.getOperand( matcher.group() ); - } - catch ( NumberFormatException ex ) - { - return ID_NOT_NUMERIC; - } - - if ( !dataElementService.dataElementExists( operand.getDataElementId() ) ) - { - return DATAELEMENT_DOES_NOT_EXIST; - } - - if ( !operand.isTotal() && !dataElementService.dataElementCategoryOptionComboExists( operand.getOptionComboId() ) ) - { - return CATEGORYOPTIONCOMBO_DOES_NOT_EXIST; - } - + String match = matcher.group(); + + if ( !DAYS_EXPRESSION.equals( match ) ) + { + try + { + operand = DataElementOperand.getOperand( match ); + } + catch ( NumberFormatException ex ) + { + return ID_NOT_NUMERIC; + } + + if ( !dataElementService.dataElementExists( operand.getDataElementId() ) ) + { + return DATAELEMENT_DOES_NOT_EXIST; + } + + if ( !operand.isTotal() && !dataElementService.dataElementCategoryOptionComboExists( operand.getOptionComboId() ) ) + { + return CATEGORYOPTIONCOMBO_DOES_NOT_EXIST; + } + } + // ----------------------------------------------------------------- // Replacing the operand with 1.1 in order to later be able to verify // that the formula is mathematically valid @@ -304,25 +311,32 @@ { String match = matcher.group(); - final DataElementOperand operand = DataElementOperand.getOperand( match ); - - final DataElement dataElement = dataElementService.getDataElement( operand.getDataElementId() ); - final DataElementCategoryOptionCombo categoryOptionCombo = - categoryService.getDataElementCategoryOptionCombo( operand.getOptionComboId() ); - - if ( dataElement == null ) - { - throw new IllegalArgumentException( "Identifier does not reference a data element: " - + operand.getDataElementId() ); - } - - if ( !operand.isTotal() && categoryOptionCombo == null ) - { - throw new IllegalArgumentException( "Identifier does not reference a category option combo: " - + operand.getOptionComboId() ); - } - - match = DataElementOperand.getPrettyName( dataElement, categoryOptionCombo ); + if ( DAYS_EXPRESSION.equals( match ) ) + { + match = DAYS_DESCRIPTION; + } + else + { + final DataElementOperand operand = DataElementOperand.getOperand( match ); + + final DataElement dataElement = dataElementService.getDataElement( operand.getDataElementId() ); + final DataElementCategoryOptionCombo categoryOptionCombo = + categoryService.getDataElementCategoryOptionCombo( operand.getOptionComboId() ); + + if ( dataElement == null ) + { + throw new IllegalArgumentException( "Identifier does not reference a data element: " + + operand.getDataElementId() ); + } + + if ( !operand.isTotal() && categoryOptionCombo == null ) + { + throw new IllegalArgumentException( "Identifier does not reference a category option combo: " + + operand.getOptionComboId() ); + } + + match = DataElementOperand.getPrettyName( dataElement, categoryOptionCombo ); + } matcher.appendReplacement( buffer, match ); } @@ -339,7 +353,7 @@ if ( expression != null ) { - final Matcher matcher = FORMULA_PATTERN.matcher( expression ); + final Matcher matcher = OPERAND_PATTERN.matcher( expression ); buffer = new StringBuffer(); @@ -372,7 +386,7 @@ return buffer != null ? buffer.toString() : null; } - public String generateExpression( String expression, Period period, OrganisationUnit source, boolean nullIfNoValues, boolean aggregated ) + public String generateExpression( String expression, Period period, OrganisationUnit source, boolean nullIfNoValues, boolean aggregated, Integer days ) { StringBuffer buffer = null; @@ -386,27 +400,34 @@ { String match = matcher.group(); - final DataElementOperand operand = DataElementOperand.getOperand( match ); - - String value = null; - - if ( aggregated ) - { - Double aggregatedValue = aggregatedDataValueService.getAggregatedDataValue( operand.getDataElementId(), operand.getOptionComboId(), period.getId(), source.getId() ); - - value = aggregatedValue != null ? String.valueOf( aggregatedValue ) : null; - } - else - { - value = dataValueService.getValue( operand.getDataElementId(), period.getId(), source.getId(), operand.getOptionComboId() ); - } - - if ( value == null && nullIfNoValues ) - { - return null; - } - - match = ( value == null ) ? NULL_REPLACEMENT : value; + if ( DAYS_EXPRESSION.equals( match ) ) // Days + { + match = days != null ? String.valueOf( days ) : NULL_REPLACEMENT; + } + else // Operand + { + final DataElementOperand operand = DataElementOperand.getOperand( match ); + + String value = null; + + if ( aggregated ) + { + Double aggregatedValue = aggregatedDataValueService.getAggregatedDataValue( operand.getDataElementId(), operand.getOptionComboId(), period.getId(), source.getId() ); + + value = aggregatedValue != null ? String.valueOf( aggregatedValue ) : null; + } + else + { + value = dataValueService.getValue( operand.getDataElementId(), period.getId(), source.getId(), operand.getOptionComboId() ); + } + + if ( value == null && nullIfNoValues ) + { + return null; + } + + match = ( value == null ) ? NULL_REPLACEMENT : value; + } matcher.appendReplacement( buffer, match ); } @@ -417,7 +438,7 @@ return buffer != null ? buffer.toString() : null; } - public String generateExpression( String expression, Map valueMap ) + public String generateExpression( String expression, Map valueMap, Integer days ) { StringBuffer buffer = null; @@ -431,11 +452,18 @@ { String match = matcher.group(); - final DataElementOperand operand = DataElementOperand.getOperand( match ); - - Double aggregatedValue = valueMap.get( operand ); - - match = ( aggregatedValue == null ) ? NULL_REPLACEMENT : String.valueOf( aggregatedValue ); + if ( DAYS_EXPRESSION.equals( match ) ) // Days + { + match = days != null ? String.valueOf( days ) : NULL_REPLACEMENT; + } + else // Operand + { + final DataElementOperand operand = DataElementOperand.getOperand( match ); + + Double aggregatedValue = valueMap.get( operand ); + + match = ( aggregatedValue == null ) ? NULL_REPLACEMENT : String.valueOf( aggregatedValue ); + } matcher.appendReplacement( buffer, match ); } === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/DefaultValidationRuleService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/DefaultValidationRuleService.java 2011-04-22 21:04:14 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/DefaultValidationRuleService.java 2011-05-20 16:00:10 +0000 @@ -302,8 +302,8 @@ { if ( validationRule.getPeriodType() != null && validationRule.getPeriodType().equals( period.getPeriodType() ) ) { - leftSide = expressionService.getExpressionValue( validationRule.getLeftSide(), period, source, true, aggregate ); - rightSide = expressionService.getExpressionValue( validationRule.getRightSide(), period, source, true, aggregate ); + leftSide = expressionService.getExpressionValue( validationRule.getLeftSide(), period, source, true, aggregate, null ); + rightSide = expressionService.getExpressionValue( validationRule.getRightSide(), period, source, true, aggregate, null ); if ( leftSide != null && rightSide != null ) { === modified file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/expression/ExpressionServiceTest.java' --- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/expression/ExpressionServiceTest.java 2011-05-20 12:27:47 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/expression/ExpressionServiceTest.java 2011-05-20 16:00:10 +0000 @@ -32,6 +32,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.hisp.dhis.expression.Expression.SEPARATOR; +import static org.hisp.dhis.expression.ExpressionService.DAYS_EXPRESSION; import java.util.Collection; import java.util.HashMap; @@ -94,6 +95,7 @@ private String expressionA; private String expressionB; private String expressionC; + private String expressionD; private String descriptionA; private String descriptionB; @@ -170,6 +172,7 @@ expressionA = "[" + dataElementIdA + SEPARATOR + categoryOptionComboId + "]+[" + dataElementIdB + SEPARATOR + categoryOptionComboId + "]"; expressionB = "[" + dataElementIdC + SEPARATOR + categoryOptionComboId + "]-[" + dataElementIdD + SEPARATOR + categoryOptionComboId + "]"; expressionC = "[" + dataElementIdA + SEPARATOR + categoryOptionComboId + "]+[" + dataElementIdE + "]-10"; + expressionD = "[" + dataElementIdA + SEPARATOR + categoryOptionComboId + "]+" + DAYS_EXPRESSION; descriptionA = "Expression A"; descriptionB = "Expression B"; @@ -223,13 +226,13 @@ { Expression expression = new Expression( expressionA, descriptionA, dataElements ); - Double value = expressionService.getExpressionValue( expression, period, source, false, false ); + Double value = expressionService.getExpressionValue( expression, period, source, false, false, null ); assertEquals( value, 15.0 ); expression = new Expression( expressionB, descriptionB, dataElements ); - value = expressionService.getExpressionValue( expression, period, source, false, false ); + value = expressionService.getExpressionValue( expression, period, source, false, false, null ); assertEquals( 0.0, value ); } @@ -314,13 +317,9 @@ @Test public void testGenerateExpression() { - String expression = expressionService.generateExpression( expressionA, period, source, false, false ); - - assertEquals( "10+5", expression ); - - expression = expressionService.generateExpression( expressionB, period, source, false, false ); - - assertEquals( "0-0", expression ); + assertEquals( "10+5", expressionService.generateExpression( expressionA, period, source, false, false, null ) ); + assertEquals( "0-0", expressionService.generateExpression( expressionB, period, source, false, false, null ) ); + assertEquals( "10+7", expressionService.generateExpression( expressionD, period, source, false, false, 7 ) ); } @Test @@ -330,7 +329,8 @@ valueMap.put( new DataElementOperand( dataElementIdA, categoryOptionComboId ), new Double( 12 ) ); valueMap.put( new DataElementOperand( dataElementIdB, categoryOptionComboId ), new Double( 34 ) ); - assertEquals( "12.0+34.0", expressionService.generateExpression( expressionA, valueMap ) ); + assertEquals( "12.0+34.0", expressionService.generateExpression( expressionA, valueMap, null ) ); + assertEquals( "12.0+5", expressionService.generateExpression( expressionD, valueMap, 5 ) ); } // ------------------------------------------------------------------------- === modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/indicator/DefaultIndicatorDataMart.java' --- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/indicator/DefaultIndicatorDataMart.java 2011-05-20 13:27:02 +0000 +++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/indicator/DefaultIndicatorDataMart.java 2011-05-20 16:00:10 +0000 @@ -30,6 +30,7 @@ import static org.hisp.dhis.options.SystemSettingManager.KEY_OMIT_INDICATORS_ZERO_NUMERATOR_DATAMART; import static org.hisp.dhis.system.util.MathUtils.calculateExpression; import static org.hisp.dhis.system.util.MathUtils.getRounded; +import static org.hisp.dhis.system.util.DateUtils.daysBetween; import java.util.Collection; import java.util.HashMap; @@ -147,6 +148,8 @@ for ( final Period period : periods ) { + int days = daysBetween( period.getStartDate(), period.getEndDate() ); + final PeriodType periodType = period.getPeriodType(); final Collection sumOperands = sumIntAggregator.filterOperands( operands, periodType ); @@ -167,8 +170,8 @@ for ( final Indicator indicator : indicators ) { - final double numeratorValue = calculateExpression( expressionService.generateExpression( indicator.getExplodedNumerator(), valueMap ) ); - final double denominatorValue = calculateExpression( expressionService.generateExpression( indicator.getExplodedDenominator(), valueMap ) ); + final double numeratorValue = calculateExpression( expressionService.generateExpression( indicator.getExplodedNumerator(), valueMap, days ) ); + final double denominatorValue = calculateExpression( expressionService.generateExpression( indicator.getExplodedDenominator(), valueMap, days ) ); // --------------------------------------------------------- // AggregatedIndicatorValue === added file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/images/no_of_days.png' Binary files dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/images/no_of_days.png 1970-01-01 00:00:00 +0000 and dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/images/no_of_days.png 2011-05-20 16:00:10 +0000 differ === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties 2011-05-13 01:26:31 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties 2011-05-20 16:00:10 +0000 @@ -133,6 +133,7 @@ divide = Devide plus = Plus minus = Minus +no_of_days = Number of days data_dictionary_intro = Please select an item from the menu. calculated = Calculated saved = Saved === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/indicatorExpressionBuilderForm.vm' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/indicatorExpressionBuilderForm.vm 2011-05-13 05:39:55 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/indicatorExpressionBuilderForm.vm 2011-05-20 16:00:10 +0000 @@ -87,7 +87,7 @@ } } - function insertText( inputAreaName, inputText, radioGroupName ) + function insertText( inputAreaName, inputText ) { insertTextCommon( inputAreaName, inputText ); @@ -180,12 +180,13 @@
- $i18n.getString( 'left_brackets' ) - $i18n.getString( 'right_brackets' ) - $i18n.getString( 'multiply' ) - $i18n.getString( 'divide' ) - $i18n.getString( 'plus' ) - $i18n.getString( 'minus' ) + + + + + + + @@ -193,7 +194,7 @@
- === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/resources/org/hisp/dhis/validationrule/i18n_module.properties' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/resources/org/hisp/dhis/validationrule/i18n_module.properties 2011-05-11 15:51:15 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/resources/org/hisp/dhis/validationrule/i18n_module.properties 2011-05-20 16:00:10 +0000 @@ -16,6 +16,7 @@ divide= Divide plus= Plus minus= Minus +no_of_days = Number of days specify_left_side = Please specify the left side specify_right_side = Please specify the right side specify_operator = Please specify an operator === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/expressionBuilderForm.vm' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/expressionBuilderForm.vm 2011-03-18 14:58:26 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/expressionBuilderForm.vm 2011-05-20 16:00:10 +0000 @@ -142,12 +142,13 @@
- $i18n.getString( 'left_brackets' ) - $i18n.getString( 'right_brackets' ) - $i18n.getString( 'multiply' ) - $i18n.getString( 'divide' ) - $i18n.getString( 'plus' ) - $i18n.getString( 'minus' ) + $i18n.getString( 'left_brackets' ) + $i18n.getString( 'right_brackets' ) + $i18n.getString( 'multiply' ) + $i18n.getString( 'divide' ) + $i18n.getString( 'plus' ) + $i18n.getString( 'minus' ) +