=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationResult.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationResult.java 2015-09-16 15:14:04 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationResult.java 2015-09-17 08:13:56 +0000 @@ -28,18 +28,17 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import java.io.Serializable; - +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import org.hisp.dhis.common.BaseIdentifiableObject; import org.hisp.dhis.common.DxfNamespaces; import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.period.Period; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import java.io.Serializable; /** * @author Margrethe Store @@ -93,7 +92,7 @@ public int hashCode() { final int prime = 31; - int result = 1; + int result = 1; result = prime * result + ((period == null) ? 0 : period.hashCode()); result = prime * result + ((orgUnit == null) ? 0 : orgUnit.hashCode()); result = prime * result + ((validationRule == null) ? 0 : validationRule.hashCode()); @@ -173,7 +172,7 @@ { return false; } - + if ( leftsideValue == null ) { if ( other.leftsideValue != null ) @@ -217,26 +216,26 @@ @Override public int compareTo( ValidationResult other ) { - int result = orgUnit.getName().compareTo( other.orgUnit.getName() ); - - if ( result != 0 ) - { - return result; - } - - result = period.getStartDate().compareTo( other.period.getStartDate() ); - - if ( result != 0 ) - { - return result; - } - - result = period.getEndDate().compareTo( other.period.getEndDate() ); - - if ( result != 0 ) - { - return result; - } + int result = orgUnit.getName().compareTo( other.orgUnit.getName() ); + + if ( result != 0 ) + { + return result; + } + + result = period.getStartDate().compareTo( other.period.getStartDate() ); + + if ( result != 0 ) + { + return result; + } + + result = period.getEndDate().compareTo( other.period.getEndDate() ); + + if ( result != 0 ) + { + return result; + } result = attributeOptionCombo.getId() - other.attributeOptionCombo.getId(); @@ -245,63 +244,63 @@ return result; } - result = validationImportanceOrder( validationRule.getImportance() ) - validationImportanceOrder( other.validationRule.getImportance() ); - - if ( result != 0 ) - { - return result; - } - - result = validationRule.getLeftSide().getDescription().compareTo( other.validationRule.getLeftSide().getDescription() ); - - if ( result != 0 ) - { - return result; - } - - result = validationRule.getOperator().compareTo( other.validationRule.getOperator() ); - - if ( result != 0 ) - { - return result; - } - - result = validationRule.getRightSide().getDescription().compareTo( other.validationRule.getRightSide().getDescription() ); - - if ( result != 0 ) - { - return result; - } - - result = (int) Math.signum( Math.round( 100.0 * leftsideValue ) - Math.round( 100.0 * other.leftsideValue ) ); - - if ( result != 0 ) - { - return result; - } - - result = (int) Math.signum( Math.round( 100.0 * rightsideValue ) - Math.round( 100.0 * other.rightsideValue ) ); - - if ( result != 0 ) - { - return result; - } - - return 0; + result = validationImportanceOrder( validationRule.getImportance() ) - validationImportanceOrder( other.validationRule.getImportance() ); + + if ( result != 0 ) + { + return result; + } + + result = validationRule.getLeftSide().getDescription().compareTo( other.validationRule.getLeftSide().getDescription() ); + + if ( result != 0 ) + { + return result; + } + + result = validationRule.getOperator().compareTo( other.validationRule.getOperator() ); + + if ( result != 0 ) + { + return result; + } + + result = validationRule.getRightSide().getDescription().compareTo( other.validationRule.getRightSide().getDescription() ); + + if ( result != 0 ) + { + return result; + } + + result = (int) Math.signum( Math.round( 100.0 * leftsideValue ) - Math.round( 100.0 * other.leftsideValue ) ); + + if ( result != 0 ) + { + return result; + } + + result = (int) Math.signum( Math.round( 100.0 * rightsideValue ) - Math.round( 100.0 * other.rightsideValue ) ); + + if ( result != 0 ) + { + return result; + } + + return 0; } - private int validationImportanceOrder( String importance ) + private int validationImportanceOrder( Importance importance ) { - return importance.equals( "high" ) ? 0 : importance.equals( "medium" ) ? 1 : 2; + return importance == Importance.HIGH ? 0 : importance == Importance.MEDIUM ? 1 : 2; } @Override public String toString() { - return "[Org unit: " + orgUnit + - ", period: " + period + - ", validation rule: " + validationRule + - ", left side value: " + leftsideValue + + return "[Org unit: " + orgUnit + + ", period: " + period + + ", validation rule: " + validationRule + + ", left side value: " + leftsideValue + ", right side value: " + rightsideValue + "]"; } === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationRule.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationRule.java 2015-07-13 12:34:39 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationRule.java 2015-09-17 08:13:56 +0000 @@ -64,13 +64,6 @@ */ private static final long serialVersionUID = -9058559806538024350L; - public static final String IMPORTANCE_HIGH = "high"; - public static final String IMPORTANCE_MEDIUM = "medium"; - public static final String IMPORTANCE_LOW = "low"; - - public static final String RULE_TYPE_VALIDATION = "validation"; - public static final String RULE_TYPE_SURVEILLANCE = "surveillance"; - /** * A description of the ValidationRule. */ @@ -84,12 +77,12 @@ /** * The user-assigned importance of this rule (e.g. high, medium or low). */ - private String importance; + private Importance importance = Importance.MEDIUM; /** * Whether this is a VALIDATION or MONITORING type rule. */ - private String ruleType; + private RuleType ruleType = RuleType.VALIDATION; /** * The comparison operator to compare left and right expressions in the rule. @@ -224,7 +217,7 @@ { Set currentDataElements = leftSide.getDataElementsInExpression(); - if ( RULE_TYPE_VALIDATION.equals( ruleType ) ) + if ( ruleType == RuleType.VALIDATION ) { currentDataElements = new HashSet<>( currentDataElements ); currentDataElements.addAll( rightSide.getDataElementsInExpression() ); @@ -242,7 +235,7 @@ */ public Set getPastDataElements() { - return RULE_TYPE_VALIDATION.equals( ruleType ) ? null : rightSide.getDataElementsInExpression(); + return ruleType == RuleType.VALIDATION ? null : rightSide.getDataElementsInExpression(); } /** @@ -316,12 +309,12 @@ @JsonProperty @JsonView( { DetailedView.class, ExportView.class } ) @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0 ) - public String getImportance() + public Importance getImportance() { - return importance != null && !importance.isEmpty() ? importance : IMPORTANCE_MEDIUM; + return importance; } - public void setImportance( String importance ) + public void setImportance( Importance importance ) { this.importance = importance; } @@ -343,12 +336,12 @@ @JsonProperty @JsonView( { DetailedView.class, ExportView.class } ) @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0 ) - public String getRuleType() + public RuleType getRuleType() { - return ruleType != null && !ruleType.isEmpty() ? ruleType : RULE_TYPE_VALIDATION; + return ruleType; } - public void setRuleType( String ruleType ) + public void setRuleType( RuleType ruleType ) { this.ruleType = ruleType; } === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/startup/InitTableAlteror.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/startup/InitTableAlteror.java 2015-09-17 04:29:29 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/startup/InitTableAlteror.java 2015-09-17 08:13:56 +0000 @@ -73,6 +73,7 @@ updateValueTypes(); updateAggregationTypes(); updateFeatureTypes(); + updateValidationRuleEnums(); executeSql( "ALTER TABLE program ALTER COLUMN \"type\" TYPE varchar(255);" ); executeSql( "update program set \"type\"='WITH_REGISTRATION' where type='1' or type='2'" ); @@ -83,6 +84,21 @@ // Supportive methods // ------------------------------------------------------------------------- + private void updateValidationRuleEnums() + { + executeSql( "alter table validationrule alter column ruletype type varchar(50)" ); + executeSql( "alter table validationrule alter column importance type varchar(50)" ); + + executeSql( "update validationrule set ruletype='VALIDATION' where ruletype='validation'" ); + executeSql( "update validationrule set ruletype='SURVEILLANCE' where ruletype='surveillance'" ); + executeSql( "update validationrule set ruletype='VALIDATION' where ruletype='' or ruletype is null" ); + + executeSql( "update validationrule set importance='HIGH' where importance='high'" ); + executeSql( "update validationrule set importance='MEDIUM' where importance='medium'" ); + executeSql( "update validationrule set importance='LOW' where importance='low'" ); + executeSql( "update validationrule set importance='MEDIUM' where importance='' or importance is null" ); + } + private void updateFeatureTypes() { executeSql( "update organisationunit set featuretype='NONE' where featuretype='None'" ); === 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 2015-06-16 05:11:29 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/DefaultValidationRuleService.java 2015-09-17 08:13:56 +0000 @@ -28,29 +28,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import static org.hisp.dhis.i18n.I18nUtils.getCountByName; -import static org.hisp.dhis.i18n.I18nUtils.getObjectsBetween; -import static org.hisp.dhis.i18n.I18nUtils.getObjectsBetweenByName; -import static org.hisp.dhis.i18n.I18nUtils.getObjectsByName; -import static org.hisp.dhis.i18n.I18nUtils.i18n; -import static org.hisp.dhis.commons.util.TextUtils.LN; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hisp.dhis.common.GenericIdentifiableObjectStore; +import org.hisp.dhis.commons.filter.Filter; +import org.hisp.dhis.commons.filter.FilterUtils; import org.hisp.dhis.constant.ConstantService; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; @@ -70,14 +53,27 @@ import org.hisp.dhis.period.PeriodService; import org.hisp.dhis.period.PeriodType; import org.hisp.dhis.setting.SystemSettingManager; -import org.hisp.dhis.commons.filter.Filter; -import org.hisp.dhis.commons.filter.FilterUtils; import org.hisp.dhis.user.CurrentUserService; import org.hisp.dhis.user.User; import org.hisp.dhis.user.UserGroup; import org.hisp.dhis.user.UserService; import org.springframework.transaction.annotation.Transactional; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import static org.hisp.dhis.commons.util.TextUtils.LN; +import static org.hisp.dhis.i18n.I18nUtils.*; + /** * @author Margrethe Store * @author Lars Helge Overland @@ -148,14 +144,14 @@ { i18nService = service; } - + private MessageService messageService; public void setMessageService( MessageService messageService ) { this.messageService = messageService; } - + private OrganisationUnitService organisationUnitService; public void setOrganisationUnitService( OrganisationUnitService organisationUnitService ) @@ -192,31 +188,31 @@ public Collection validate( Date startDate, Date endDate, Collection sources, DataElementCategoryOptionCombo attributeCombo, ValidationRuleGroup group, boolean sendAlerts, I18nFormat format ) { - log.info( "Validate start:" + startDate + " end: " + endDate + " sources: " + sources.size() + " group: " + group ); - + log.info( "Validate start:" + startDate + " end: " + endDate + " sources: " + sources.size() + " group: " + group ); + Collection periods = periodService.getPeriodsBetweenDates( startDate, endDate ); Collection rules = group != null ? group.getMembers() : getAllValidationRules(); - + Collection results = Validator.validate( sources, periods, rules, attributeCombo, null, constantService, expressionService, periodService, dataValueService, dataElementCategoryService, userService, currentUserService ); formatPeriods( results, format ); - + if ( sendAlerts ) { Set resultsToAlert = new HashSet<>( results ); FilterUtils.filter( resultsToAlert, new ValidationResultToAlertFilter() ); postAlerts( resultsToAlert, new Date() ); } - + return results; } @Override public Collection validate( Date startDate, Date endDate, OrganisationUnit source ) { - log.info( "Validate start: " + startDate + " end: " + endDate + " source: " + source.getName() ); - + log.info( "Validate start: " + startDate + " end: " + endDate + " source: " + source.getName() ); + Collection periods = periodService.getPeriodsBetweenDates( startDate, endDate ); Collection rules = getAllValidationRules(); Collection sources = new HashSet<>(); @@ -230,20 +226,20 @@ public Collection validate( DataSet dataSet, Period period, OrganisationUnit source, DataElementCategoryOptionCombo attributeCombo ) { - log.info( "Validate data set: " + dataSet.getName() + " period: " + period.getPeriodType().getName() + " " + log.info( "Validate data set: " + dataSet.getName() + " period: " + period.getPeriodType().getName() + " " + period.getStartDate() + " " + period.getEndDate() + " source: " + source.getName() - + " attribute combo: " + ( attributeCombo == null ? "[none]" : attributeCombo.getName() ) ); + + " attribute combo: " + (attributeCombo == null ? "[none]" : attributeCombo.getName()) ); Collection periods = new ArrayList<>(); periods.add( period ); Collection rules = getValidationTypeRulesForDataElements( dataSet.getDataElements() ); - + log.debug( "Using validation rules: " + rules.size() ); - + Collection sources = new HashSet<>(); sources.add( source ); - + return Validator.validate( sources, periods, rules, attributeCombo, null, constantService, expressionService, periodService, dataValueService, dataElementCategoryService, userService, currentUserService ); } @@ -252,36 +248,36 @@ public void scheduledRun() { log.info( "Starting scheduled monitoring task" ); - + // Find all the rules belonging to groups that will send alerts to user roles. Set rules = getAlertRules(); Collection sources = organisationUnitService.getAllOrganisationUnits(); - + Set periods = getAlertPeriodsFromRules( rules ); - + Date lastScheduledRun = (Date) systemSettingManager.getSystemSetting( SystemSettingManager.KEY_LAST_MONITORING_RUN ); - + // Any database changes after this moment will contribute to the next run. - + Date thisRun = new Date(); - + log.info( "Scheduled monitoring run sources: " + sources.size() + ", periods: " + periods.size() + ", rules:" + rules.size() - + ", last run: " + ( lastScheduledRun == null ? "[none]" : lastScheduledRun ) ); - + + ", last run: " + (lastScheduledRun == null ? "[none]" : lastScheduledRun) ); + Collection results = Validator.validate( sources, periods, rules, null, lastScheduledRun, - constantService, expressionService, periodService, dataValueService, dataElementCategoryService, userService, currentUserService ); - + constantService, expressionService, periodService, dataValueService, dataElementCategoryService, userService, currentUserService ); + log.info( "Validation run result count: " + results.size() ); - + if ( !results.isEmpty() ) { postAlerts( results, thisRun ); } - + log.info( "Posted alerts, monitoring task done" ); - + systemSettingManager.saveSystemSetting( SystemSettingManager.KEY_LAST_MONITORING_RUN, thisRun ); } @@ -289,7 +285,7 @@ public List validateRequiredComments( DataSet dataSet, Period period, OrganisationUnit organisationUnit, DataElementCategoryOptionCombo attributeOptionCombo ) { List violations = new ArrayList<>(); - + if ( dataSet.isNoValueRequiresComment() ) { for ( DataElement de : dataSet.getDataElements() ) @@ -297,10 +293,10 @@ for ( DataElementCategoryOptionCombo co : de.getCategoryCombo().getOptionCombos() ) { DataValue dv = dataValueService.getDataValue( de, period, organisationUnit, co, attributeOptionCombo ); - + boolean missingValue = dv == null || StringUtils.trimToNull( dv.getValue() ) == null; boolean missingComment = dv == null || StringUtils.trimToNull( dv.getComment() ) == null; - + if ( missingValue && missingComment ) { violations.add( new DataElementOperand( de, co ) ); @@ -308,7 +304,7 @@ } } } - + return violations; } @@ -319,7 +315,7 @@ for ( ValidationRule validationRule : getAllValidationRules() ) { - if ( validationRule.getRuleType().equals( ValidationRule.RULE_TYPE_VALIDATION ) ) + if ( validationRule.getRuleType() == RuleType.VALIDATION ) { Set validationRuleElements = new HashSet<>(); validationRuleElements.addAll( validationRule.getLeftSide().getDataElementsInExpression() ); @@ -334,20 +330,20 @@ return rulesForDataElements; } - + // ------------------------------------------------------------------------- // Supportive methods - scheduled run // ------------------------------------------------------------------------- /** * Gets all the validation rules that could generate alerts. - * + * * @return rules that will generate alerts */ private Set getAlertRules() { Set rules = new HashSet<>(); - + for ( ValidationRuleGroup validationRuleGroup : getAllValidationRuleGroups() ) { if ( validationRuleGroup.hasUserGroupsToAlert() ) @@ -355,19 +351,19 @@ rules.addAll( validationRuleGroup.getMembers() ); } } - + return rules; } /** * Gets the current and most recent periods to search, based on * the period types from the rules to run. - * + *

* For each period type, return the period containing the current date * (if any), and the most recent previous period. Add whichever of * these periods actually exist in the database. - * - * TODO If the last successful daily run was more than one day ago, we might + *

+ * TODO If the last successful daily run was more than one day ago, we might * add some additional periods of type DailyPeriodType not to miss any * alerts. * @@ -382,11 +378,11 @@ for ( PeriodType periodType : rulePeriodTypes ) { - CalendarPeriodType calendarPeriodType = ( CalendarPeriodType ) periodType; + CalendarPeriodType calendarPeriodType = (CalendarPeriodType) periodType; Period currentPeriod = calendarPeriodType.createPeriod(); Period previousPeriod = calendarPeriodType.getPreviousPeriod( currentPeriod ); periods.addAll( periodService.getIntersectingPeriodsByPeriodType( periodType, - previousPeriod.getStartDate(), currentPeriod.getEndDate() ) ); + previousPeriod.getStartDate(), currentPeriod.getEndDate() ) ); } return periods; @@ -395,19 +391,19 @@ /** * At the end of a scheduled monitoring run, post messages to the users who * want to see the results. - * + *

* Create one message for each set of users who receive the same * subset of results. (Not necessarily the same as the set of users who * receive alerts from the same subset of validation rules -- because * some of these rules may return no results.) This saves on message * storage space. - * + *

* The message results are sorted into their natural order. - * + *

* TODO: Internationalize the messages according to the user's * preferred language, and generate a message for each combination of * ( target language, set of results ). - * + * * @param validationResults the set of validation error results * @param scheduledRunStart the date/time when this scheduled run started */ @@ -425,22 +421,22 @@ /** * Gets the Set of period types found in a set of rules. - * + *

* Note that that we have to get periodType from periodService, * otherwise the ID will not be present.) - * + * * @param rules validation rules of interest * @return period types contained in those rules */ - private Set getPeriodTypesFromRules ( Collection rules ) + private Set getPeriodTypesFromRules( Collection rules ) { Set rulePeriodTypes = new HashSet<>(); - + for ( ValidationRule rule : rules ) { rulePeriodTypes.add( periodService.getPeriodTypeByName( rule.getPeriodType().getName() ) ); } - + return rulePeriodTypes; } @@ -448,7 +444,7 @@ * Returns a map where the key is a sorted list of validation results * to assemble into a message, and the value is the set of users who * should receive this message. - * + * * @param results all the validation run results, in a sorted set * @return map of result sets to users */ @@ -468,7 +464,7 @@ messageMap.put( userResultEntry.getValue(), users ); } - + users.add( userResultEntry.getKey() ); } @@ -498,7 +494,7 @@ { if ( !ruleGroup.isAlertByOrgUnits() || canUserAccessSource( user, result.getOrgUnit() ) ) { - SortedSet resultSet = userResults.get ( user ); + SortedSet resultSet = userResults.get( user ); if ( resultSet == null ) { @@ -506,7 +502,7 @@ userResults.put( user, resultSet ); } - + resultSet.add( result ); } } @@ -521,7 +517,7 @@ * Determines whether a user can access an organisation unit, * based on the organisation units to which the user has been assigned. * - * @param user user to test + * @param user user to test * @param source organisation unit to which the user may have access * @return whether the user has acceess to the organisation unit */ @@ -534,16 +530,16 @@ return true; } } - + return false; } /** * Generate and send an alert message containing a list of validation * results to a set of users. - * - * @param results results to put in this message - * @param users users to receive these results + * + * @param results results to put in this message + * @param users users to receive these results * @param scheduledRunStart date/time when the scheduled run started */ private void sendAlertmessage( SortedSet results, Set users, Date scheduledRunStart ) @@ -552,28 +548,28 @@ SimpleDateFormat dateTimeFormatter = new SimpleDateFormat( "yyyy-MM-dd HH:mm" ); - Map importanceCountMap = countResultsByImportanceType( results ); + Map importanceCountMap = countResultsByImportanceType( results ); String subject = "Alerts as of " + dateTimeFormatter.format( scheduledRunStart ) + ": High " - + ( importanceCountMap.get( "high" ) == null ? 0 : importanceCountMap.get( "high" ) ) + ", Medium " - + ( importanceCountMap.get( "medium" ) == null ? 0 : importanceCountMap.get( "medium" ) ) + ", Low " - + ( importanceCountMap.get( "low" ) == null ? 0 : importanceCountMap.get( "low" ) ); + + (importanceCountMap.get( Importance.HIGH ) == null ? 0 : importanceCountMap.get( Importance.HIGH )) + ", Medium " + + (importanceCountMap.get( Importance.MEDIUM ) == null ? 0 : importanceCountMap.get( Importance.MEDIUM )) + ", Low " + + (importanceCountMap.get( Importance.LOW ) == null ? 0 : importanceCountMap.get( Importance.LOW )); //TODO use velocity template for message - + for ( ValidationResult result : results ) { ValidationRule rule = result.getValidationRule(); - + builder.append( result.getOrgUnit().getName() ).append( " " ).append( result.getPeriod().getName() ). - append( result.getAttributeOptionCombo().isDefault() ? "" : " " + result.getAttributeOptionCombo().getName() ).append( LN ). - append( rule.getName() ).append( " (" ).append( rule.getImportance() ).append( ") " ).append( LN ). - append( rule.getLeftSide().getDescription() ).append( ": " ).append( result.getLeftsideValue() ).append( LN ). - append( rule.getRightSide().getDescription() ).append( ": " ).append( result.getRightsideValue() ).append( LN ).append( LN ); + append( result.getAttributeOptionCombo().isDefault() ? "" : " " + result.getAttributeOptionCombo().getName() ).append( LN ). + append( rule.getName() ).append( " (" ).append( rule.getImportance() ).append( ") " ).append( LN ). + append( rule.getLeftSide().getDescription() ).append( ": " ).append( result.getLeftsideValue() ).append( LN ). + append( rule.getRightSide().getDescription() ).append( ": " ).append( result.getRightsideValue() ).append( LN ).append( LN ); } - + log.info( "Alerting users: " + users.size() + ", subject: " + subject ); - + messageService.sendMessage( subject, builder.toString(), null, users ); } @@ -584,30 +580,29 @@ /** * Counts the results of each importance type, for all the importance * types that are found within the results. - * + * * @param results results to analyze * @return Mapping between importance type and result counts. */ - private Map countResultsByImportanceType ( Set results ) + private Map countResultsByImportanceType( Set results ) { - Map importanceCountMap = new HashMap<>(); - + Map importanceCountMap = new HashMap<>(); + for ( ValidationResult result : results ) { Integer importanceCount = importanceCountMap.get( result.getValidationRule().getImportance() ); - - importanceCountMap.put( result.getValidationRule().getImportance(), importanceCount == null ? 1 - : importanceCount + 1 ); + + importanceCountMap.put( result.getValidationRule().getImportance(), importanceCount == null ? 1 : importanceCount + 1 ); } - + return importanceCountMap; } - + /** * Formats and sets name on the period of each result. - * + * * @param results the collection of validation results. - * @param format the i18n format. + * @param format the i18n format. */ private void formatPeriods( Collection results, I18nFormat format ) { @@ -622,7 +617,7 @@ } } } - + // ------------------------------------------------------------------------- // ValidationRule CRUD operations // ------------------------------------------------------------------------- === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/ValidationRunContext.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/ValidationRunContext.java 2015-02-19 09:18:17 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/ValidationRunContext.java 2015-09-17 08:13:56 +0000 @@ -28,15 +28,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; - import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.logging.Log; @@ -57,20 +48,29 @@ import org.hisp.dhis.user.UserCredentials; import org.hisp.dhis.user.UserService; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; + /** * Holds common values that are used during a validation run (either interactive * or scheduled.) These values don't change during the multi-threaded tasks * (except that results entries are added in a threadsafe way.) - * + *

* Some of the values are precalculated collections, to save CPU time during the * run. All of these values are stored in this single "context" object to allow * a single object reference for each of the scheduled tasks. (This also reduces * the amount of memory needed to queue all the multi-threaded tasks.) - * + *

* For some of these properties this is also important because they should be * copied from Hibernate lazy collections before the multithreaded part of the * run starts, otherwise the threads may not be able to access these values. - * + * * @author Jim Grace */ public class ValidationRunContext @@ -90,7 +90,7 @@ private Collection sourceXs; private DataElementCategoryOptionCombo attributeCombo; - + private int countOfSourcesToValidate; private Set cogDimensionConstraints; @@ -114,7 +114,7 @@ public String toString() { return new ToStringBuilder( this, ToStringStyle.SHORT_PREFIX_STYLE ) - .append( "\n PeriodTypeExtendedMap", ( Arrays.toString( periodTypeExtendedMap.entrySet().toArray() ) ) ) + .append( "\n PeriodTypeExtendedMap", (Arrays.toString( periodTypeExtendedMap.entrySet().toArray() )) ) .append( "\n runType", runType ) .append( "\n lastScheduledRun", lastScheduledRun ) .append( "\n constantMap", "[" + constantMap.size() + "]" ) @@ -125,20 +125,19 @@ /** * Creates and fills a new context object for a validation run. - * - * @param sources organisation units for validation - * @param periods periods for validation - * @param attributeCombo the attribute combo to check (if restricted) - * @param rules validation rules for validation - * @param runType whether this is an INTERACTIVE or SCHEDULED run - * @param lastScheduledRun (for SCHEDULED runs) date/time of previous run - * @param expressionService expression service - * @param periodService period service - * @param dataValueService data value service + * + * @param sources organisation units for validation + * @param periods periods for validation + * @param attributeCombo the attribute combo to check (if restricted) + * @param rules validation rules for validation + * @param runType whether this is an INTERACTIVE or SCHEDULED run + * @param lastScheduledRun (for SCHEDULED runs) date/time of previous run + * @param expressionService expression service + * @param periodService period service + * @param dataValueService data value service * @param dataElementCategoryService data element category service - * @param userService user service - * @param currentUserService current user service - * + * @param userService user service + * @param currentUserService current user service * @return context object for this run */ public static ValidationRunContext getNewContext( Collection sources, @@ -166,30 +165,30 @@ context.cogDimensionConstraints = userService.getCogDimensionConstraints( currentUserCredentials ); context.coDimensionConstraints = userService.getCoDimensionConstraints( currentUserCredentials ); context.initialize( sources, periods, rules ); - + return context; } /** * Initializes context values based on sources, periods and rules - * + * * @param sources organisation units to evaluate for rules * @param periods periods for validation - * @param rules validation rules for validation + * @param rules validation rules for validation */ private void initialize( Collection sources, Collection periods, Collection rules ) { addPeriodsToContext( periods ); - + boolean surveillanceRulesPresent = addRulesToContext( rules ); - + removeAnyUnneededPeriodTypes(); - + addSourcesToContext( sources, true ); - + countOfSourcesToValidate = sources.size(); - + if ( surveillanceRulesPresent ) { Set otherDescendants = getAllOtherDescendants( sources ); @@ -199,31 +198,31 @@ /** * Adds Periods to the context, grouped by period type. - * + * * @param periods Periods to group and add */ - private void addPeriodsToContext ( Collection periods ) + private void addPeriodsToContext( Collection periods ) { for ( Period period : periods ) - { - PeriodTypeExtended periodTypeX = getOrCreatePeriodTypeExtended( period.getPeriodType() ); - periodTypeX.getPeriods().add( period ); - } + { + PeriodTypeExtended periodTypeX = getOrCreatePeriodTypeExtended( period.getPeriodType() ); + periodTypeX.getPeriods().add( period ); + } } /** * Adds validation rules to the context. - * + * * @param rules validation rules to add * @return true if there were some surveillance-type rules, false otherwise. */ - private boolean addRulesToContext ( Collection rules ) + private boolean addRulesToContext( Collection rules ) { - boolean surveillanceRulesPresent = false; - + boolean surveillanceRulesPresent = false; + for ( ValidationRule rule : rules ) { - if ( ValidationRule.RULE_TYPE_SURVEILLANCE.equals( rule.getRuleType() ) ) + if ( rule.getRuleType() == RuleType.SURVEILLANCE ) { if ( rule.getOrganisationUnitLevel() == null ) { @@ -231,7 +230,7 @@ + "' has no organisationUnitLevel." ); continue; // Ignore rule, avoid null reference later. } - + surveillanceRulesPresent = true; } @@ -244,7 +243,7 @@ // Add this rule's data elements to the period extended. periodTypeX.getDataElements().addAll( rule.getCurrentDataElements() ); } - + // Add the allowed period types for rule's current data elements: periodTypeX.getAllowedPeriodTypes().addAll( getAllowedPeriodTypesForDataElements( rule.getCurrentDataElements(), rule.getPeriodType() ) ); @@ -255,7 +254,7 @@ ValidationRuleExtended ruleX = new ValidationRuleExtended( rule, allowedPastPeriodTypes ); ruleXMap.put( rule, ruleX ); } - + return surveillanceRulesPresent; } @@ -265,7 +264,7 @@ private void removeAnyUnneededPeriodTypes() { Set periodTypeXs = new HashSet<>( periodTypeExtendedMap.values() ); - + for ( PeriodTypeExtended periodTypeX : periodTypeXs ) { if ( periodTypeX.getRules().isEmpty() ) @@ -280,7 +279,7 @@ * collection of organisation units. This is needed for surveillance-type * rules, because the data values for the rules may need to be aggregated * from the organisation unit's descendants. - * + *

* The descendants will likely be there anyway for a run including * surveillance-type rules, because an interactive run containing * surveillance-type rules should select an entire subtree, and a @@ -288,31 +287,31 @@ * just to be sure, and find any that may be missing. This makes sure * that some of the tests will work, and may be required for some * future features to work. - * + * * @param sources organisation units whose descendants to check * @return all other descendants who need to be added who were not * in the original list */ private Set getAllOtherDescendants( Collection sources ) { - Set allOtherDescendants = new HashSet<>(); - + Set allOtherDescendants = new HashSet<>(); + for ( OrganisationUnit source : sources ) { getOtherDescendantsRecursive( source, sources, allOtherDescendants ); } - + return allOtherDescendants; } /** * If the children of this organisation unit are not in the collection, then * add them and all their descendants if needed. - * - * @param source organisation unit whose children to check - * @param sources organisation units in the initial list + * + * @param source organisation unit whose children to check + * @param sources organisation units in the initial list * @param allOtherDescendants list of organisation unit descendants we - * need to add + * need to add */ private void getOtherDescendantsRecursive( OrganisationUnit source, Collection sources, Set allOtherDescendants ) @@ -321,7 +320,7 @@ { if ( !sources.contains( child ) && !allOtherDescendants.contains( child ) ) { - allOtherDescendants.add( child ); + allOtherDescendants.add( child ); getOtherDescendantsRecursive( child, sources, allOtherDescendants ); } } @@ -329,15 +328,15 @@ /** * Adds a collection of organisation units to the validation run context. - * - * @param sources organisation units to add + * + * @param sources organisation units to add * @param ruleCheckThisSource true if these organisation units should be - * evaluated with validation rules, false if not. (This is false when - * adding descendants of organisation units for the purpose of getting - * aggregated expression values from descendants, but these organisation - * units are not in the main list to be evaluated.) + * evaluated with validation rules, false if not. (This is false when + * adding descendants of organisation units for the purpose of getting + * aggregated expression values from descendants, but these organisation + * units are not in the main list to be evaluated.) */ - private void addSourcesToContext ( Collection sources, boolean ruleCheckThisSource ) + private void addSourcesToContext( Collection sources, boolean ruleCheckThisSource ) { for ( OrganisationUnit source : sources ) { @@ -345,15 +344,15 @@ sourceXs.add( sourceX ); Map> sourceElementsMap = source.getDataElementsInDataSetsByPeriodType(); - + for ( PeriodTypeExtended periodTypeX : periodTypeExtendedMap.values() ) { - periodTypeX.getSourceDataElements().put( source, new HashSet() ); - + periodTypeX.getSourceDataElements().put( source, new HashSet<>() ); + for ( PeriodType allowedType : periodTypeX.getAllowedPeriodTypes() ) { Collection sourceDataElements = sourceElementsMap.get( allowedType ); - + if ( sourceDataElements != null ) { periodTypeX.getSourceDataElements().get( source ).addAll( sourceDataElements ); @@ -362,41 +361,41 @@ } } } - + /** * Gets the PeriodTypeExtended from the context object. If not found, * creates a new PeriodTypeExtended object, puts it into the context object, * and returns it. - * + * * @param periodType period type to search for * @return period type extended from the context object */ private PeriodTypeExtended getOrCreatePeriodTypeExtended( PeriodType periodType ) { PeriodTypeExtended periodTypeX = periodTypeExtendedMap.get( periodType ); - + if ( periodTypeX == null ) { periodTypeX = new PeriodTypeExtended( periodType ); periodTypeExtendedMap.put( periodType, periodTypeX ); } - + return periodTypeX; } /** * Finds all period types that may contain given data elements, whose period * type interval is at least as long as the given period type. - * + * * @param dataElements data elements to look for - * @param periodType the minimum-length period type + * @param periodType the minimum-length period type * @return all period types that are allowed for these data elements */ private static Collection getAllowedPeriodTypesForDataElements( Collection dataElements, PeriodType periodType ) { Collection allowedPeriodTypes = new HashSet<>(); - + if ( dataElements != null ) { for ( DataElement dataElement : dataElements ) @@ -410,7 +409,7 @@ } } } - + return allowedPeriodTypes; } === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/ValidatorThread.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/ValidatorThread.java 2015-09-13 17:45:53 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/ValidatorThread.java 2015-09-17 08:13:56 +0000 @@ -28,26 +28,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import static org.hisp.dhis.system.util.MathUtils.expressionIsTrue; -import static org.hisp.dhis.system.util.MathUtils.roundSignificant; -import static org.hisp.dhis.system.util.MathUtils.zeroIfNull; - -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hisp.dhis.common.ListMap; import org.hisp.dhis.common.MapMap; import org.hisp.dhis.common.SetMap; +import org.hisp.dhis.commons.util.DebugUtils; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.dataelement.DataElementOperand; import org.hisp.dhis.expression.Expression; @@ -56,12 +42,24 @@ import org.hisp.dhis.period.CalendarPeriodType; import org.hisp.dhis.period.Period; import org.hisp.dhis.period.PeriodType; -import org.hisp.dhis.commons.util.DebugUtils; import org.hisp.dhis.system.util.MathUtils; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.hisp.dhis.system.util.MathUtils.*; + /** * Runs a validation task on a thread within a multi-threaded validation run. - * + *

* Each task looks for validation results in a different organisation unit. * * @author Jim Grace @@ -95,26 +93,26 @@ catch ( RuntimeException ex ) { log.error( DebugUtils.getStackTrace( ex ) ); - + throw ex; } } - + private void runInternal() { - if ( context.getValidationResults().size() < ( ValidationRunType.INTERACTIVE == context.getRunType() ? + if ( context.getValidationResults().size() < (ValidationRunType.INTERACTIVE == context.getRunType() ? ValidationRuleService.MAX_INTERACTIVE_ALERTS : ValidationRuleService.MAX_SCHEDULED_ALERTS) ) { for ( PeriodTypeExtended periodTypeX : context.getPeriodTypeExtendedMap().values() ) { Collection sourceDataElements = periodTypeX.getSourceDataElements().get( sourceX.getSource() ); - Set rules = getRulesBySourceAndPeriodType( sourceX, periodTypeX, sourceDataElements ); + Set rules = getRulesBySourceAndPeriodType( sourceX, periodTypeX, sourceDataElements ); context.getExpressionService().explodeValidationRuleExpressions( rules ); if ( !rules.isEmpty() ) { Set recursiveCurrentDataElements = getRecursiveCurrentDataElements( rules ); - + for ( Period period : periodTypeX.getPeriods() ) { MapMap lastUpdatedMap = new MapMap<>(); @@ -122,7 +120,7 @@ MapMap currentValueMap = getValueMap( periodTypeX, periodTypeX.getDataElements(), sourceDataElements, recursiveCurrentDataElements, periodTypeX.getAllowedPeriodTypes(), period, sourceX.getSource(), lastUpdatedMap, incompleteValuesMap ); - + log.trace( "Source " + sourceX.getSource().getName() + " [" + period.getStartDate() + " - " + period.getEndDate() + "]" + " currentValueMap[" + currentValueMap.size() + "]" ); @@ -132,7 +130,7 @@ if ( evaluateValidationCheck( currentValueMap, lastUpdatedMap, rule ) ) { Map leftSideValues = getExpressionValueMap( rule.getLeftSide(), - currentValueMap, incompleteValuesMap ); + currentValueMap, incompleteValuesMap ); if ( !leftSideValues.isEmpty() || Operator.compulsory_pair.equals( rule.getOperator() ) ) { @@ -142,7 +140,7 @@ if ( !rightSideValues.isEmpty() || Operator.compulsory_pair.equals( rule.getOperator() ) ) { Set attributeOptionCombos = leftSideValues.keySet(); - + if ( Operator.compulsory_pair.equals( rule.getOperator() ) ) { attributeOptionCombos = new HashSet<>( attributeOptionCombos ); @@ -157,8 +155,8 @@ if ( Operator.compulsory_pair.equals( rule.getOperator() ) ) { - violation = ( leftSide != null && rightSide == null ) - || ( leftSide == null && rightSide != null ); + violation = (leftSide != null && rightSide == null) + || (leftSide == null && rightSide != null); } else if ( leftSide != null && rightSide != null ) { @@ -176,8 +174,8 @@ log.trace( "Evaluated " + rule.getName() + ", combo id " + optionCombo + ": " - + (violation ? "violation" : "OK") + " " + ( leftSide == null ? "(null)" : leftSide.toString() ) - + " " + rule.getOperator() + " " + ( rightSide == null ? "(null)" : rightSide.toString() ) + + (violation ? "violation" : "OK") + " " + (leftSide == null ? "(null)" : leftSide.toString()) + + " " + rule.getOperator() + " " + (rightSide == null ? "(null)" : rightSide.toString()) + " (" + context.getValidationResults().size() + " results)" ); } } @@ -193,11 +191,11 @@ /** * Gets the rules that should be evaluated for a given organisation unit and * period type. - * - * @param sourceX the organisation unit extended information - * @param periodTypeX the period type extended information + * + * @param sourceX the organisation unit extended information + * @param periodTypeX the period type extended information * @param sourceDataElements all data elements collected for this - * organisation unit + * organisation unit * @return set of rules for this org unit and period type */ private Set getRulesBySourceAndPeriodType( OrganisationUnitExtended sourceX, @@ -207,14 +205,14 @@ for ( ValidationRule rule : periodTypeX.getRules() ) { - if ( ( ValidationRule.RULE_TYPE_VALIDATION.equals( rule.getRuleType() ) ) ) + if ( rule.getRuleType() == RuleType.VALIDATION ) { // For validation-type rules, include only rules where the // organisation collects all the data elements in the rule. // But if this is some funny kind of rule with no elements // (like for testing), include it also. Collection elements = rule.getCurrentDataElements(); - + if ( elements == null || elements.size() == 0 || sourceDataElements.containsAll( elements ) ) { periodTypeRules.add( rule ); @@ -232,7 +230,7 @@ } } } - + return periodTypeRules; } @@ -243,12 +241,12 @@ * SCHEDULED runs, we go further only if something has changed since the * last successful scheduled run -- either the rule definition or one of * the "current" data element / option values on the left or right sides. - * + *

* For scheduled runs, remove all values for any attribute option combos * where nothing has changed since the last run. * * @param lastUpdatedMapMap when each data value was last updated - * @param rule the rule that may be evaluated + * @param rule the rule that may be evaluated * @return true if the rule should be evaluated with this data, false if not */ private boolean evaluateValidationCheck( MapMap currentValueMapMap, @@ -267,8 +265,8 @@ // SURVEILLANCE. Collection deos = context.getExpressionService().getOperandsInExpression( rule.getLeftSide().getExpression() ); - - if ( ValidationRule.RULE_TYPE_VALIDATION.equals( rule.getRuleType() ) ) + + if ( rule.getRuleType() == RuleType.VALIDATION ) { // Make a copy so we can add to it. deos = new HashSet<>( deos ); @@ -286,7 +284,7 @@ for ( DataElementOperand deo : deos ) { Date lastUpdated = entry.getValue().get( deo ); - + if ( lastUpdated != null && lastUpdated.after( context.getLastScheduledRun() ) ) { saveThisCombo = true; // True if new/updated data. @@ -309,7 +307,7 @@ /** * Gets the data elements for which values should be fetched recursively if * they are not collected for an organisation unit. - * + * * @param rules ValidationRules to be evaluated * @return the data elements to fetch recursively */ @@ -319,26 +317,25 @@ for ( ValidationRule rule : rules ) { - if ( ValidationRule.RULE_TYPE_SURVEILLANCE.equals( rule.getRuleType() ) - && rule.getCurrentDataElements() != null ) + if ( rule.getRuleType() == RuleType.SURVEILLANCE && rule.getCurrentDataElements() != null ) { recursiveCurrentDataElements.addAll( rule.getCurrentDataElements() ); } } - + return recursiveCurrentDataElements; } /** * Returns the right-side evaluated value of the validation rule. - * - * @param source organisation unit being evaluated - * @param periodTypeX period type being evaluated - * @param period period being evaluated - * @param rule ValidationRule being evaluated - * @param currentValueMap current values already fetched + * + * @param source organisation unit being evaluated + * @param periodTypeX period type being evaluated + * @param period period being evaluated + * @param rule ValidationRule being evaluated + * @param currentValueMap current values already fetched * @param sourceDataElements the data elements collected by the organisation - * unit + * unit * @return the right-side values, map by attribute category combo */ private Map getRightSideValue( OrganisationUnit source, PeriodTypeExtended periodTypeX, Period period, @@ -353,16 +350,15 @@ // values we use, so just supply the current data values in order to // evaluate the (constant) expression. - if ( ValidationRule.RULE_TYPE_VALIDATION.equals( rule.getRuleType() ) - || rule.getRightSide().getDataElementsInExpression().isEmpty() ) + if ( rule.getRuleType() == RuleType.VALIDATION || rule.getRightSide().getDataElementsInExpression().isEmpty() ) { - rightSideValues = getExpressionValueMap( rule.getRightSide(), currentValueMap, new SetMap() ); + rightSideValues = getExpressionValueMap( rule.getRightSide(), currentValueMap, new SetMap<>() ); } else // ruleType equals SURVEILLANCE, and there are some data elements in the // right side expression { - CalendarPeriodType calendarPeriodType = ( CalendarPeriodType ) period.getPeriodType(); + CalendarPeriodType calendarPeriodType = (CalendarPeriodType) period.getPeriodType(); Collection rightSidePeriodTypes = context.getRuleXMap().get( rule ).getAllowedPastPeriodTypes(); ListMap sampleValuesMap = new ListMap<>(); Calendar yearlyCalendar = PeriodType.createCalendarInstance( period.getStartDate() ); @@ -374,7 +370,7 @@ { // Defensive copy because createPeriod mutates Calendar. Calendar calCopy = PeriodType.createCalendarInstance( yearlyCalendar.getTime() ); - + // To track the period at the same time in preceding years. Period yearlyPeriod = calendarPeriodType.createPeriod( calCopy ); @@ -393,7 +389,7 @@ // Fetch the sequential periods after this prior-year // period. Period sequentialPeriod = new Period( yearlyPeriod ); - + for ( int sequentialCount = 0; sequentialCount < sequentialSampleCount; sequentialCount++ ) { sequentialPeriod = calendarPeriodType.getNextPeriod( sequentialPeriod ); @@ -405,7 +401,7 @@ // Fetch the sequential periods before this period (both this // year and past years). Period sequentialPeriod = new Period( yearlyPeriod ); - + for ( int sequentialCount = 0; sequentialCount < sequentialSampleCount; sequentialCount++ ) { sequentialPeriod = calendarPeriodType.getPreviousPeriod( sequentialPeriod ); @@ -418,10 +414,10 @@ } rightSideValues = new HashMap<>(); - + for ( Map.Entry> e : sampleValuesMap.entrySet() ) { - rightSideValues.put( e.getKey(), rightSideAverage( rule, e.getValue(), annualSampleCount, sequentialSampleCount) ); + rightSideValues.put( e.getKey(), rightSideAverage( rule, e.getValue(), annualSampleCount, sequentialSampleCount ) ); } } @@ -432,19 +428,19 @@ * Evaluates the right side of a surveillance-type validation rule for * a given organisation unit and period, and adds the value to a list * of sample values. - * + *

* Note that for a surveillance-type rule, evaluating the right side * expression can result in sampling multiple periods and/or child * organisation units. - * - * @param periodTypeX the period type extended information - * @param sampleValuesMap the lists of sample values to add to - * @param source the organisation unit + * + * @param periodTypeX the period type extended information + * @param sampleValuesMap the lists of sample values to add to + * @param source the organisation unit * @param allowedPeriodTypes the period types in which the data may exist - * @param period the main period for the validation rule evaluation - * @param rule the surveillance-type rule being evaluated + * @param period the main period for the validation rule evaluation + * @param rule the surveillance-type rule being evaluated * @param sourceDataElements the data elements configured for this - * organisation unit + * organisation unit */ private void evaluateRightSidePeriod( PeriodTypeExtended periodTypeX, ListMap sampleValuesMap, OrganisationUnit source, Collection allowedPeriodTypes, Period period, ValidationRule rule, @@ -452,7 +448,7 @@ { Period periodInstance = context.getPeriodService().getPeriod( period.getStartDate(), period.getEndDate(), period.getPeriodType() ); - + if ( periodInstance != null ) { Set dataElements = rule.getRightSide().getDataElementsInExpression(); @@ -467,13 +463,13 @@ * Evaluates an expression, returning a map of values by attribute option * combo. * - * @param expression expression to evaluate. - * @param valueMap Map of value maps, by attribute option combo. + * @param expression expression to evaluate. + * @param valueMap Map of value maps, by attribute option combo. * @param incompleteValuesMap map of values that were incomplete. * @return map of values. */ private Map getExpressionValueMap( Expression expression, - MapMap valueMap, + MapMap valueMap, SetMap incompleteValuesMap ) { Map expressionValueMap = new HashMap<>(); @@ -481,9 +477,9 @@ for ( Map.Entry> entry : valueMap.entrySet() ) { Double value = context.getExpressionService().getExpressionValue( expression, - entry.getValue(), context.getConstantMap(), null, null, + entry.getValue(), context.getConstantMap(), null, null, incompleteValuesMap.getSet( entry.getKey() ) ); - + if ( MathUtils.isValidDouble( value ) ) { expressionValueMap.put( entry.getKey(), value ); @@ -496,14 +492,14 @@ /** * Finds the average right-side sample value. This is used as the right-side * expression value to evaluate a surveillance-type rule. - * - * @param rule surveillance-type rule being evaluated - * @param sampleValues sample values actually collected - * @param annualSampleCount number of annual samples tried for + * + * @param rule surveillance-type rule being evaluated + * @param sampleValues sample values actually collected + * @param annualSampleCount number of annual samples tried for * @param sequentialSampleCount number of sequential samples tried for * @return average right-side sample value */ - private Double rightSideAverage( ValidationRule rule, List sampleValues, + private Double rightSideAverage( ValidationRule rule, List sampleValues, int annualSampleCount, int sequentialSampleCount ) { // Find the expected sample count for the last period of its type in the @@ -511,7 +507,7 @@ // in this year and for every past year: one sample for the same period // in that year, plus sequentialSampleCounts before and after. Double average = null; - + if ( !sampleValues.isEmpty() ) { int expectedSampleCount = sequentialSampleCount + annualSampleCount * (1 + 2 * sequentialSampleCount); @@ -535,37 +531,37 @@ sampleValues = sampleValues.subList( lowOutliers, sampleValues.size() - highOutliers ); log.trace( "Result: " + Arrays.toString( sampleValues.toArray() ) ); } - + Double sum = 0.0; - + for ( Double sample : sampleValues ) { sum += sample; } - + average = sum / sampleValues.size(); } - + return average; } /** * Gets data values for a given organisation unit and period, recursing if * necessary to sum the values from child organisation units. - * - * @param periodTypeX period type which we are evaluating - * @param ruleDataElements data elements configured for the rule - * @param sourceDataElements data elements configured for the organisation - * unit + * + * @param periodTypeX period type which we are evaluating + * @param ruleDataElements data elements configured for the rule + * @param sourceDataElements data elements configured for the organisation + * unit * @param recursiveDataElements data elements for which we will recurse if - * necessary - * @param allowedPeriodTypes all the periods in which we might find the data - * values - * @param period period in which we are looking for values - * @param source organisation unit for which we are looking for values - * @param lastUpdatedMap map showing when each data values was last updated - * @param incompleteValuesMap ongoing set showing which values were found - * but not from all children, mapped by attribute option combo. + * necessary + * @param allowedPeriodTypes all the periods in which we might find the data + * values + * @param period period in which we are looking for values + * @param source organisation unit for which we are looking for values + * @param lastUpdatedMap map showing when each data values was last updated + * @param incompleteValuesMap ongoing set showing which values were found + * but not from all children, mapped by attribute option combo. * @return map of attribute option combo to map of values found. */ private MapMap getValueMap( PeriodTypeExtended periodTypeX, @@ -576,7 +572,7 @@ { Set dataElementsToGet = new HashSet<>( ruleDataElements ); dataElementsToGet.retainAll( sourceDataElements ); - + log.trace( "getDataValueMapRecursive: source:" + source.getName() + " ruleDataElements[" + ruleDataElements.size() + "] sourceDataElements[" + sourceDataElements.size() @@ -585,7 +581,7 @@ + "] allowedPeriodTypes[" + allowedPeriodTypes.size() + "]" ); MapMap dataValueMap = null; - + if ( dataElementsToGet.isEmpty() ) { // We still might get something recursively @@ -597,16 +593,16 @@ period.getStartDate(), source, allowedPeriodTypes, context.getAttributeCombo(), context.getCogDimensionConstraints(), context.getCoDimensionConstraints(), lastUpdatedMap ); } - + // See if there are any data elements we need to get recursively: Set recursiveDataElementsNeeded = new HashSet<>( recursiveDataElements ); recursiveDataElementsNeeded.removeAll( dataElementsToGet ); - + if ( !recursiveDataElementsNeeded.isEmpty() ) { int childCount = 0; MapMap childValueCounts = new MapMap<>(); - + for ( OrganisationUnit child : source.getChildren() ) { Collection childDataElements = periodTypeX.getSourceDataElements().get( child ); === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/validation/hibernate/ValidationRule.hbm.xml' --- dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/validation/hibernate/ValidationRule.hbm.xml 2015-01-09 13:25:59 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/validation/hibernate/ValidationRule.hbm.xml 2015-09-17 08:13:56 +0000 @@ -3,7 +3,7 @@ "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" [] -> + > @@ -18,12 +18,24 @@ - + - + + + org.hisp.dhis.validation.Importance + true + 12 + + - + + + org.hisp.dhis.validation.RuleType + true + 12 + + @@ -37,19 +49,19 @@ - + - + - + - + - + - + === modified file 'dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/csv/DefaultCsvImportService.java' --- dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/csv/DefaultCsvImportService.java 2015-09-17 04:29:29 +0000 +++ dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/csv/DefaultCsvImportService.java 2015-09-17 08:13:56 +0000 @@ -55,6 +55,8 @@ import org.hisp.dhis.organisationunit.OrganisationUnitGroup; import org.hisp.dhis.period.MonthlyPeriodType; import org.hisp.dhis.period.PeriodType; +import org.hisp.dhis.validation.Importance; +import org.hisp.dhis.validation.RuleType; import org.hisp.dhis.validation.ValidationRule; import org.springframework.beans.factory.annotation.Autowired; @@ -274,8 +276,8 @@ setIdentifiableObject( object, values ); object.setDescription( getSafe( values, 3, null, 255 ) ); object.setInstruction( getSafe( values, 4, null, 255 ) ); - object.setImportance( getSafe( values, 5, ValidationRule.IMPORTANCE_MEDIUM, 255 ) ); - object.setRuleType( getSafe( values, 6, ValidationRule.RULE_TYPE_VALIDATION, 255 ) ); + object.setImportance( Importance.valueOf( getSafe( values, 5, Importance.MEDIUM.toString(), 255 ) ) ); + object.setRuleType( RuleType.valueOf( getSafe( values, 6, RuleType.VALIDATION.toString(), 255 ) ) ); object.setOperator( Operator.safeValueOf( getSafe( values, 7, Operator.equal_to.toString(), 255 ) ) ); object.setPeriodType( PeriodType.getByNameIgnoreCase( getSafe( values, 8, MonthlyPeriodType.NAME, 255 ) ) ); === modified file 'dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/DhisConvenienceTest.java' --- dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/DhisConvenienceTest.java 2015-09-15 12:25:44 +0000 +++ dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/DhisConvenienceTest.java 2015-09-17 08:13:56 +0000 @@ -92,6 +92,7 @@ import org.hisp.dhis.user.UserCredentials; import org.hisp.dhis.user.UserGroup; import org.hisp.dhis.user.UserService; +import org.hisp.dhis.validation.RuleType; import org.hisp.dhis.validation.ValidationCriteria; import org.hisp.dhis.validation.ValidationRule; import org.hisp.dhis.validation.ValidationRuleGroup; @@ -982,7 +983,7 @@ validationRule.setName( "MonitoringRule" + uniqueCharacter ); validationRule.setDescription( "Description" + uniqueCharacter ); - validationRule.setRuleType( ValidationRule.RULE_TYPE_SURVEILLANCE ); + validationRule.setRuleType( RuleType.SURVEILLANCE ); validationRule.setOperator( operator ); validationRule.setLeftSide( leftSide ); validationRule.setRightSide( rightSide ); === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/AddValidationRuleAction.java' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/AddValidationRuleAction.java 2015-03-11 11:01:16 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/AddValidationRuleAction.java 2015-09-17 08:13:56 +0000 @@ -28,23 +28,24 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import static org.hisp.dhis.expression.MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING; -import static org.hisp.dhis.expression.MissingValueStrategy.safeValueOf; - +import com.opensymphony.xwork2.Action; import org.apache.commons.lang3.StringUtils; import org.hisp.dhis.expression.Expression; import org.hisp.dhis.expression.ExpressionService; import org.hisp.dhis.expression.Operator; import org.hisp.dhis.period.PeriodService; import org.hisp.dhis.period.PeriodType; +import org.hisp.dhis.validation.Importance; +import org.hisp.dhis.validation.RuleType; import org.hisp.dhis.validation.ValidationRule; import org.hisp.dhis.validation.ValidationRuleService; -import com.opensymphony.xwork2.Action; +import static org.hisp.dhis.expression.MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING; +import static org.hisp.dhis.expression.MissingValueStrategy.safeValueOf; /** * Adds a new validation rule to the database. - * + * * @author Margrethe Store * @author Lars Helge Overland * @version $Id: AddValidationRuleAction.java 3868 2007-11-08 15:11:12Z larshelg $ @@ -62,25 +63,25 @@ { this.validationRuleService = validationRuleService; } - + private ExpressionService expressionService; - + public void setExpressionService( ExpressionService expressionService ) { this.expressionService = expressionService; } - + private PeriodService periodService; public void setPeriodService( PeriodService periodService ) { this.periodService = periodService; } - + // ------------------------------------------------------------------------- // Input // ------------------------------------------------------------------------- - + private String name; public void setName( String name ) @@ -96,7 +97,7 @@ } private String instruction; - + public void setInstruction( String instruction ) { this.instruction = instruction; @@ -138,7 +139,7 @@ } private String leftSideMissingValueStrategy; - + public void setLeftSideMissingValueStrategy( String leftSideMissingValueStrategy ) { this.leftSideMissingValueStrategy = leftSideMissingValueStrategy; @@ -150,14 +151,14 @@ { this.rightSideExpression = rightSideExpression; } - + private String rightSideDescription; public void setRightSideDescription( String rightSideDescription ) { this.rightSideDescription = rightSideDescription; } - + private String rightSideMissingValueStrategy; public void setRightSideMissingValueStrategy( String rightSideMissingValueStrategy ) @@ -166,43 +167,43 @@ } private Integer organisationUnitLevel; - + public void setOrganisationUnitLevel( Integer organisationUnitLevel ) { this.organisationUnitLevel = organisationUnitLevel; } private String periodTypeName; - - public void setPeriodTypeName(String periodTypeName) + + public void setPeriodTypeName( String periodTypeName ) { this.periodTypeName = periodTypeName; } - + private Integer sequentialSampleCount; - - public void setSequentialSampleCount(Integer sequentialSampleCount) + + public void setSequentialSampleCount( Integer sequentialSampleCount ) { this.sequentialSampleCount = sequentialSampleCount; } private Integer annualSampleCount; - - public void setAnnualSampleCount(Integer annualSampleCount) + + public void setAnnualSampleCount( Integer annualSampleCount ) { this.annualSampleCount = annualSampleCount; } private Integer highOutliers; - - public void setHighOutliers(Integer highOutliers) + + public void setHighOutliers( Integer highOutliers ) { this.highOutliers = highOutliers; } private Integer lowOutliers; - - public void setLowOutliers(Integer lowOutliers) + + public void setLowOutliers( Integer lowOutliers ) { this.lowOutliers = lowOutliers; } @@ -210,45 +211,45 @@ // ------------------------------------------------------------------------- // Action implementation // ------------------------------------------------------------------------- - + @Override public String execute() { Expression leftSide = new Expression(); - + leftSide.setExpression( leftSideExpression ); leftSide.setDescription( leftSideDescription ); leftSide.setMissingValueStrategy( safeValueOf( leftSideMissingValueStrategy, SKIP_IF_ANY_VALUE_MISSING ) ); leftSide.setDataElementsInExpression( expressionService.getDataElementsInExpression( leftSideExpression ) ); - + Expression rightSide = new Expression(); - + rightSide.setExpression( rightSideExpression ); rightSide.setDescription( rightSideDescription ); rightSide.setMissingValueStrategy( safeValueOf( rightSideMissingValueStrategy, SKIP_IF_ANY_VALUE_MISSING ) ); rightSide.setDataElementsInExpression( expressionService.getDataElementsInExpression( rightSideExpression ) ); - + ValidationRule validationRule = new ValidationRule(); - + validationRule.setName( StringUtils.trimToNull( name ) ); validationRule.setDescription( StringUtils.trimToNull( description ) ); validationRule.setInstruction( StringUtils.trimToNull( instruction ) ); - validationRule.setImportance( StringUtils.trimToNull( importance ) ); - validationRule.setRuleType( StringUtils.trimToNull( ruleType ) ); + validationRule.setImportance( Importance.valueOf( StringUtils.trimToNull( importance ) ) ); + validationRule.setRuleType( RuleType.valueOf( StringUtils.trimToNull( ruleType ) ) ); validationRule.setOperator( Operator.valueOf( operator ) ); validationRule.setLeftSide( leftSide ); validationRule.setRightSide( rightSide ); validationRule.setOrganisationUnitLevel( organisationUnitLevel ); - PeriodType periodType = periodService.getPeriodTypeByName(periodTypeName); - validationRule.setPeriodType(periodType); + PeriodType periodType = periodService.getPeriodTypeByName( periodTypeName ); + validationRule.setPeriodType( periodType ); validationRule.setSequentialSampleCount( sequentialSampleCount ); validationRule.setAnnualSampleCount( annualSampleCount ); validationRule.setHighOutliers( highOutliers ); validationRule.setLowOutliers( lowOutliers ); validationRuleService.saveValidationRule( validationRule ); - + return SUCCESS; } } === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/ExportValidationResultAction.java' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/ExportValidationResultAction.java 2015-02-19 09:18:17 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/ExportValidationResultAction.java 2015-09-17 08:13:56 +0000 @@ -122,7 +122,7 @@ return type; } - @SuppressWarnings("unchecked") + @SuppressWarnings( "unchecked" ) private Grid generateGrid() { List results = (List) SessionUtils. @@ -159,8 +159,8 @@ grid.addValue( unit.getName() ); grid.addValue( format.formatPeriod( period ) ); grid.addValue( validationResult.getValidationRule().getName() ); - grid.addValue( i18n.getString( validationResult.getValidationRule().getImportance() ) ); - grid.addValue( i18n.getString( validationResult.getValidationRule().getRuleType() ) ); + grid.addValue( i18n.getString( validationResult.getValidationRule().getImportance().toString().toLowerCase() ) ); + grid.addValue( i18n.getString( validationResult.getValidationRule().getRuleType().toString().toLowerCase() ) ); grid.addValue( validationResult.getValidationRule().getLeftSide().getDescription() ); //TODO lazy prone grid.addValue( String.valueOf( validationResult.getLeftsideValue() ) ); grid.addValue( i18n.getString( validationResult.getValidationRule().getOperator().toString() ) ); === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/UpdateValidationRuleAction.java' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/UpdateValidationRuleAction.java 2015-03-11 11:01:16 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/UpdateValidationRuleAction.java 2015-09-17 08:13:56 +0000 @@ -28,18 +28,19 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import static org.hisp.dhis.expression.MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING; -import static org.hisp.dhis.expression.MissingValueStrategy.safeValueOf; - +import com.opensymphony.xwork2.Action; import org.apache.commons.lang3.StringUtils; import org.hisp.dhis.expression.ExpressionService; import org.hisp.dhis.expression.Operator; import org.hisp.dhis.period.PeriodService; import org.hisp.dhis.period.PeriodType; +import org.hisp.dhis.validation.Importance; +import org.hisp.dhis.validation.RuleType; import org.hisp.dhis.validation.ValidationRule; import org.hisp.dhis.validation.ValidationRuleService; -import com.opensymphony.xwork2.Action; +import static org.hisp.dhis.expression.MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING; +import static org.hisp.dhis.expression.MissingValueStrategy.safeValueOf; /** * @author Margrethe Store @@ -97,7 +98,7 @@ { this.description = description; } - + private String instruction; public void setInstruction( String instruction ) @@ -111,7 +112,7 @@ { this.importance = importance; } - + private String ruleType; public void setRuleType( String ruleType ) @@ -141,7 +142,7 @@ } private String leftSideMissingValueStrategy; - + public void setLeftSideMissingValueStrategy( String leftSideMissingValueStrategy ) { this.leftSideMissingValueStrategy = leftSideMissingValueStrategy; @@ -169,7 +170,7 @@ } private Integer organisationUnitLevel; - + public void setOrganisationUnitLevel( Integer organisationUnitLevel ) { this.organisationUnitLevel = organisationUnitLevel; @@ -222,8 +223,8 @@ validationRule.setName( StringUtils.trimToNull( name ) ); validationRule.setDescription( StringUtils.trimToNull( description ) ); validationRule.setInstruction( StringUtils.trimToNull( instruction ) ); - validationRule.setImportance( StringUtils.trimToNull( importance ) ); - validationRule.setRuleType( StringUtils.trimToNull( ruleType ) ); + validationRule.setImportance( Importance.valueOf( StringUtils.trimToNull( importance ) ) ); + validationRule.setRuleType( RuleType.valueOf( StringUtils.trimToNull( ruleType ) ) ); validationRule.setOperator( Operator.valueOf( operator ) ); validationRule.getLeftSide().setExpression( leftSideExpression ); @@ -236,7 +237,7 @@ validationRule.getRightSide().setMissingValueStrategy( safeValueOf( rightSideMissingValueStrategy, SKIP_IF_ANY_VALUE_MISSING ) ); validationRule.getRightSide().setDataElementsInExpression( expressionService.getDataElementsInExpression( rightSideExpression ) ); validationRule.setOrganisationUnitLevel( organisationUnitLevel ); - + PeriodType periodType = periodService.getPeriodTypeByName( periodTypeName ); validationRule.setPeriodType( periodType == null ? null : periodService.getPeriodTypeByClass( periodType.getClass() ) ); === 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 2014-12-20 13:15:34 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/resources/org/hisp/dhis/validationrule/i18n_module.properties 2015-09-17 08:13:56 +0000 @@ -48,6 +48,7 @@ source= Source done= Done validation=Validation +VALIDATION=Validation data_quality_report=Data quality report validation_result_details=Validation result details validation_rule_=Validation rule @@ -138,10 +139,13 @@ high=High medium=Medium low=Low +HIGH=High +MEDIUM=Medium +LOW=Low rule_type=Rule type +surveillance=Surveillance +SURVEILLANCE=Surveillance attributes=Attributes -validation=Validation -surveillance=Surveillance organisation_unit_level=Organisation unit level select_level=Select level sequential_sample_count=Sequential sample count === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/addValidationRuleForm.vm' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/addValidationRuleForm.vm 2014-08-28 12:01:44 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/addValidationRuleForm.vm 2015-09-17 08:13:56 +0000 @@ -21,9 +21,9 @@ @@ -31,8 +31,8 @@ === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/javascript/validationRule.js' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/javascript/validationRule.js 2013-12-06 13:37:28 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/javascript/validationRule.js 2015-09-17 08:13:56 +0000 @@ -1,7 +1,7 @@ function changeRuleType() { var ruleType = $('#ruleType').val(); - if( ruleType == 'validation' ) { + if( ruleType == 'VALIDATION' ) { hideById('organisationUnitLevelTR'); hideById('sequentialSampleCountTR'); hideById('annualSampleCountTR'); === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/updateValidationRuleForm.vm' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/updateValidationRuleForm.vm 2014-08-28 12:01:44 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/updateValidationRuleForm.vm 2015-09-17 08:13:56 +0000 @@ -31,9 +31,9 @@ @@ -41,8 +41,8 @@ === modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/validationRule.vm' --- dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/validationRule.vm 2014-06-13 08:33:45 +0000 +++ dhis-2/dhis-web/dhis-web-validationrule/src/main/webapp/dhis-web-validationrule/validationRule.vm 2015-09-17 08:13:56 +0000 @@ -87,7 +87,7 @@ data-can-update="$security.canUpdate( $validationRule )" data-can-delete="#if($auth.hasAccess( "dhis-web-validationrule", "removeValidationRule" ) )true#{else}false#end"> $encoder.htmlEncode( $!validationRule.displayName ) - $encoder.htmlEncode( $i18n.getString($!validationRule.importance) ) + $encoder.htmlEncode( $i18n.getString("$!validationRule.importance") ) $encoder.htmlEncode( $i18n.getString($!validationRule.periodType.name) ) #end