dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #25760
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 12807: Validation/monitoring. Moved methods inMonitoringTask inside ValidationRuleService to make the fu...
------------------------------------------------------------
revno: 12807
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2013-10-24 00:03:17 +0200
message:
Validation/monitoring. Moved methods inMonitoringTask inside ValidationRuleService to make the function run inside a transaction, avoiding hibernate session problems.
modified:
dhis-2/dhis-api/src/main/java/org/hisp/dhis/scheduling/TaskCategory.java
dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationRuleService.java
dhis-2/dhis-services/dhis-service-core/pom.xml
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/Validator.java
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/scheduling/MonitoringTask.java
dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml
dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/ResourceTableController.java
--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk
Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/scheduling/TaskCategory.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/scheduling/TaskCategory.java 2013-09-04 11:58:22 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/scheduling/TaskCategory.java 2013-10-23 22:03:17 +0000
@@ -35,6 +35,8 @@
{
DATAMART,
RESOURCETABLE_UPDATE,
+ ANALYTICSTABLE_UPDATE,
+ MONITORING,
DATAVALUE_IMPORT,
EVENT_IMPORT,
METADATA_IMPORT,
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationRuleService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationRuleService.java 2013-10-16 13:41:01 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/validation/ValidationRuleService.java 2013-10-23 22:03:17 +0000
@@ -92,6 +92,12 @@
*/
Collection<ValidationResult> validate( Date startDate, Date endDate, OrganisationUnit source );
+ /**
+ * Evaluates all the validation rules that could generate alerts,
+ * and sends results (if any) to users who should be notified.
+ */
+ void scheduledRun();
+
// -------------------------------------------------------------------------
// ValidationRule
// -------------------------------------------------------------------------
=== modified file 'dhis-2/dhis-services/dhis-service-core/pom.xml'
--- dhis-2/dhis-services/dhis-service-core/pom.xml 2013-10-17 06:57:37 +0000
+++ dhis-2/dhis-services/dhis-service-core/pom.xml 2013-10-23 22:03:17 +0000
@@ -92,7 +92,6 @@
<groupId>org.smslib</groupId>
<artifactId>smslib</artifactId>
</dependency>
-
<dependency>
<groupId>com.googlecode.jsmpp</groupId>
<artifactId>jsmpp</artifactId>
=== 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 2013-10-21 14:19:10 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/DefaultValidationRuleService.java 2013-10-23 22:03:17 +0000
@@ -34,11 +34,17 @@
import static org.hisp.dhis.i18n.I18nUtils.getObjectsByName;
import static org.hisp.dhis.i18n.I18nUtils.i18n;
+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.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -51,11 +57,19 @@
import org.hisp.dhis.datavalue.DataValueService;
import org.hisp.dhis.expression.ExpressionService;
import org.hisp.dhis.i18n.I18nService;
+import org.hisp.dhis.message.MessageService;
import org.hisp.dhis.organisationunit.OrganisationUnit;
+import org.hisp.dhis.organisationunit.OrganisationUnitService;
+import org.hisp.dhis.period.CalendarPeriodType;
import org.hisp.dhis.period.Period;
import org.hisp.dhis.period.PeriodService;
+import org.hisp.dhis.period.PeriodType;
+import org.hisp.dhis.setting.SystemSettingManager;
import org.hisp.dhis.system.util.Filter;
import org.hisp.dhis.system.util.FilterUtils;
+import org.hisp.dhis.user.User;
+import org.hisp.dhis.user.UserAuthorityGroup;
+import org.hisp.dhis.user.UserCredentials;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -128,11 +142,33 @@
{
i18nService = service;
}
+
+ private MessageService messageService;
+
+ public void setMessageService( MessageService messageService )
+ {
+ this.messageService = messageService;
+ }
+
+ private OrganisationUnitService organisationUnitService;
+
+ public void setOrganisationUnitService( OrganisationUnitService organisationUnitService )
+ {
+ this.organisationUnitService = organisationUnitService;
+ }
+
+ private SystemSettingManager systemSettingManager;
+
+ public void setSystemSettingManager( SystemSettingManager systemSettingManager )
+ {
+ this.systemSettingManager = systemSettingManager;
+ }
// -------------------------------------------------------------------------
// ValidationRule business logic
// -------------------------------------------------------------------------
+ @Override
public Collection<ValidationResult> validate( Date startDate, Date endDate, Collection<OrganisationUnit> sources )
{
log.info( "Validate startDate=" + startDate + " endDate=" + endDate + " sources[" + sources.size() + "]" );
@@ -143,6 +179,7 @@
constantService, expressionService, periodService, dataValueService );
}
+ @Override
public Collection<ValidationResult> validate( Date startDate, Date endDate, Collection<OrganisationUnit> sources,
ValidationRuleGroup group )
{
@@ -154,6 +191,7 @@
constantService, expressionService, periodService, dataValueService );
}
+ @Override
public Collection<ValidationResult> validate( Date startDate, Date endDate, OrganisationUnit source )
{
log.info( "Validate startDate=" + startDate + " endDate=" + endDate + " source=" + source.getName() );
@@ -166,6 +204,7 @@
constantService, expressionService, periodService, dataValueService );
}
+ @Override
public Collection<ValidationResult> validate( DataSet dataSet, Period period, OrganisationUnit source )
{
log.info( "Validate dataSet=" + dataSet.getName() + " period=[" + period.getPeriodType().getName() + " "
@@ -191,10 +230,322 @@
constantService, expressionService, periodService, dataValueService );
}
- // -------------------------------------------------------------------------
- // Support methods
- // -------------------------------------------------------------------------
-
+ @Override
+ public void scheduledRun()
+ {
+ // Find all the rules belonging to groups that will send alerts to user roles.
+
+ Set<ValidationRule> rules = getAlertRules();
+
+ Collection<OrganisationUnit> sources = organisationUnitService.getAllOrganisationUnits();
+ Set<Period> 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 );
+
+ Collection<ValidationResult> results = Validator.validate( sources, periods, rules, ValidationRunType.SCHEDULED,
+ lastScheduledRun, constantService, expressionService, periodService, dataValueService );
+
+ log.trace( "scheduledRun() results[" + results.size() + "]" );
+
+ if ( !results.isEmpty() )
+ {
+ postAlerts( results, thisRun );
+ }
+
+ systemSettingManager.saveSystemSetting( SystemSettingManager.KEY_LAST_MONITORING_RUN, thisRun );
+ }
+
+ // -------------------------------------------------------------------------
+ // Supportive methods - scheduled run
+ // -------------------------------------------------------------------------
+
+ /**
+ * Gets all the validation rules that could generate alerts.
+ *
+ * @return rules that will generate alerts
+ */
+ private Set<ValidationRule> getAlertRules()
+ {
+ Set<ValidationRule> rules = new HashSet<ValidationRule>();
+
+ for ( ValidationRuleGroup validationRuleGroup : getAllValidationRuleGroups() )
+ {
+ Set<UserAuthorityGroup> userRolesToAlert = validationRuleGroup.getUserAuthorityGroupsToAlert();
+
+ if ( userRolesToAlert != null && !userRolesToAlert.isEmpty() )
+ {
+ 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
+ * add some additional periods of type DailyPeriodType not to miss any
+ * alerts.
+ *
+ * @param rules the ValidationRules to be evaluated on this run
+ * @return periods to search for new alerts
+ */
+ private Set<Period> getAlertPeriodsFromRules( Set<ValidationRule> rules )
+ {
+ Set<Period> periods = new HashSet<Period>();
+
+ Set<PeriodType> rulePeriodTypes = getPeriodTypesFromRules( rules );
+
+ for ( PeriodType periodType : rulePeriodTypes )
+ {
+ CalendarPeriodType calendarPeriodType = ( CalendarPeriodType ) periodType;
+ Period currentPeriod = calendarPeriodType.createPeriod();
+ Period previousPeriod = calendarPeriodType.getPreviousPeriod( currentPeriod );
+ periods.addAll( periodService.getIntersectingPeriodsByPeriodType( periodType,
+ previousPeriod.getStartDate(), currentPeriod.getEndDate() ) );
+ }
+
+ return periods;
+ }
+
+ /**
+ * 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
+ */
+ private void postAlerts( Collection<ValidationResult> validationResults, Date scheduledRunStart )
+ {
+ SortedSet<ValidationResult> results = new TreeSet<ValidationResult>( validationResults );
+
+ Map<List<ValidationResult>, Set<User>> messageMap = getMessageMap( results );
+
+ for ( Map.Entry<List<ValidationResult>, Set<User>> entry : messageMap.entrySet() )
+ {
+ sendAlertmessage( entry.getKey(), entry.getValue(), scheduledRunStart );
+ }
+ }
+
+ /**
+ * 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<PeriodType> getPeriodTypesFromRules ( Collection<ValidationRule> rules )
+ {
+ Set<PeriodType> rulePeriodTypes = new HashSet<PeriodType>();
+
+ for ( ValidationRule rule : rules )
+ {
+ rulePeriodTypes.add( periodService.getPeriodTypeByName( rule.getPeriodType().getName() ) );
+ }
+
+ return rulePeriodTypes;
+ }
+
+ /**
+ * 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
+ * @return map of result sets to users
+ */
+ private Map<List<ValidationResult>, Set<User>> getMessageMap( Set<ValidationResult> results )
+ {
+ Map<User, Set<ValidationRule>> userRulesMap = getUserRulesMap();
+
+ Map<List<ValidationResult>, Set<User>> messageMap = new HashMap<List<ValidationResult>, Set<User>>();
+
+ for ( User user : userRulesMap.keySet() )
+ {
+ // For users receiving alerts, find the subset of results from run.
+
+ Collection<ValidationRule> userRules = userRulesMap.get( user );
+ List<ValidationResult> userResults = new ArrayList<ValidationResult>();
+
+ for ( ValidationResult result : results )
+ {
+ if ( userRules.contains( result.getValidationRule() ) )
+ {
+ userResults.add( result );
+ }
+ }
+
+ // Group this user with other users having the same result subset.
+
+ if ( !userResults.isEmpty() )
+ {
+ Set<User> messageReceivers = messageMap.get( userResults );
+ if ( messageReceivers == null )
+ {
+ messageReceivers = new HashSet<User>();
+ messageMap.put( userResults, messageReceivers );
+ }
+ messageReceivers.add( user );
+ }
+ }
+
+ return messageMap;
+ }
+
+ /**
+ * Constructs a Map where the key is each user who is configured to
+ * receive alerts, and the value is a list of rules they should receive
+ * results for.
+ *
+ * @return Map from users to sets of rules
+ */
+ private Map<User, Set<ValidationRule>> getUserRulesMap()
+ {
+ Map<User, Set<ValidationRule>> userRulesMap = new HashMap<User, Set<ValidationRule>>();
+
+ for ( ValidationRuleGroup validationRuleGroup : getAllValidationRuleGroups() )
+ {
+ Collection<UserAuthorityGroup> userRolesToAlert = validationRuleGroup.getUserAuthorityGroupsToAlert();
+
+ if ( userRolesToAlert != null && !userRolesToAlert.isEmpty() )
+ {
+ for ( UserAuthorityGroup role : userRolesToAlert )
+ {
+ for ( UserCredentials userCredentials : role.getMembers() )
+ {
+ User user = userCredentials.getUser();
+ Set<ValidationRule> userRules = userRulesMap.get( user );
+ if ( userRules == null )
+ {
+ userRules = new HashSet<ValidationRule>();
+ userRulesMap.put( user, userRules );
+ }
+ userRules.addAll( validationRuleGroup.getMembers() );
+ }
+ }
+ }
+ }
+
+ return userRulesMap;
+ }
+
+ /**
+ * 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 scheduledRunStart date/time when the scheduled run started
+ */
+ private void sendAlertmessage( List<ValidationResult> results, Set<User> users, Date scheduledRunStart )
+ {
+ StringBuilder messageBuilder = new StringBuilder();
+
+ SimpleDateFormat dateTimeFormatter = new SimpleDateFormat( "yyyy-MM-dd HH:mm" );
+
+ Map<String, Integer> importanceCountMap = countResultsByImportanceType( results );
+
+ String subject = "DHIS alerts as of " + dateTimeFormatter.format( scheduledRunStart ) + " - priority 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" ) );
+
+ messageBuilder
+ .append( "<html>" )
+ .append( "<head>" ).append( "</head>" )
+ .append( "<body>" ).append( subject ).append( "<br />" )
+ .append( "<table>" )
+ .append( "<tr>" )
+ .append( "<th>Organisation Unit</th>" )
+ .append( "<th>Period</th>" )
+ .append( "<th>Importance</th>" )
+ .append( "<th>Left side description</th>" )
+ .append( "<th>Value</th>" )
+ .append( "<th>Operator</th>" )
+ .append( "<th>Value</th>" )
+ .append( "<th>Right side description</th>" )
+ .append( "</tr>" );
+
+ for ( ValidationResult result : results )
+ {
+ ValidationRule rule = result.getValidationRule();
+
+ messageBuilder
+ .append( "<tr>" )
+ .append( "<td>" ).append( result.getSource().getName() ).append( "<\td>" )
+ .append( "<td>" ).append( result.getPeriod().getName() ).append( "<\td>" )
+ .append( "<td>" ).append( rule.getImportance() ).append( "<\td>" )
+ .append( "<td>" ).append( rule.getLeftSide().getDescription() ).append( "<\td>" )
+ .append( "<td>" ).append( result.getLeftsideValue() ).append( "<\td>" )
+ .append( "<td>" ).append( rule.getOperator().toString() ).append( "<\td>" )
+ .append( "<td>" ).append( result.getRightsideValue() ).append( "<\td>" )
+ .append( "<td>" ).append( rule.getRightSide().getDescription() ).append( "<\td>" )
+ .append( "</tr>" );
+ }
+
+ messageBuilder
+ .append( "</table>" )
+ .append( "</body>" )
+ .append( "</html>" );
+
+ String messageText = messageBuilder.toString();
+
+ log.info( "Alerting users[" + users.size() + "] subject " + subject );
+ messageService.sendMessage( subject, messageText, null, users );
+ }
+
+ // -------------------------------------------------------------------------
+ // Supportive methods - monitoring
+ // -------------------------------------------------------------------------
+
+ /**
+ * 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<String, Integer> countResultsByImportanceType ( List<ValidationResult> results )
+ {
+ Map<String, Integer> importanceCountMap = new HashMap<String, Integer>();
+
+ for ( ValidationResult result : results )
+ {
+ Integer importanceCount = importanceCountMap.get( result.getValidationRule().getImportance() );
+
+ importanceCountMap.put( result.getValidationRule().getImportance(), importanceCount == null ? 1
+ : importanceCount + 1 );
+ }
+
+ return importanceCountMap;
+ }
+
/**
* Returns all validation-type rules which have specified data elements
* assigned to them.
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/Validator.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/Validator.java 2013-10-16 16:00:05 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/Validator.java 2013-10-23 22:03:17 +0000
@@ -69,7 +69,7 @@
*/
public static Collection<ValidationResult> validate( Collection<OrganisationUnit> sources,
Collection<Period> periods, Collection<ValidationRule> rules, ValidationRunType runType, Date lastScheduledRun,
- ConstantService constantService, ExpressionService expressionService, PeriodService periodService, DataValueService dataValueService)
+ ConstantService constantService, ExpressionService expressionService, PeriodService periodService, DataValueService dataValueService )
{
ValidationRunContext context = ValidationRunContext.getNewValidationRunContext( sources, periods, rules,
constantService.getConstantMap(), ValidationRunType.SCHEDULED, lastScheduledRun,
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/scheduling/MonitoringTask.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/scheduling/MonitoringTask.java 2013-10-16 16:00:05 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/validation/scheduling/MonitoringTask.java 2013-10-23 22:03:17 +0000
@@ -31,42 +31,16 @@
import static org.hisp.dhis.system.notification.NotificationLevel.ERROR;
import static org.hisp.dhis.system.notification.NotificationLevel.INFO;
-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.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.hisp.dhis.constant.ConstantService;
import org.hisp.dhis.datavalue.DataValueService;
import org.hisp.dhis.expression.ExpressionService;
import org.hisp.dhis.message.MessageService;
-import org.hisp.dhis.organisationunit.OrganisationUnit;
import org.hisp.dhis.organisationunit.OrganisationUnitService;
-import org.hisp.dhis.period.CalendarPeriodType;
-import org.hisp.dhis.period.Period;
import org.hisp.dhis.period.PeriodService;
-import org.hisp.dhis.period.PeriodType;
import org.hisp.dhis.scheduling.TaskId;
import org.hisp.dhis.setting.SystemSettingManager;
import org.hisp.dhis.system.notification.Notifier;
-import org.hisp.dhis.user.User;
-import org.hisp.dhis.user.UserAuthorityGroup;
-import org.hisp.dhis.user.UserCredentials;
-import org.hisp.dhis.validation.ValidationResult;
-import org.hisp.dhis.validation.ValidationRule;
-import org.hisp.dhis.validation.ValidationRuleGroup;
import org.hisp.dhis.validation.ValidationRuleService;
-import org.hisp.dhis.validation.ValidationRunType;
-import org.hisp.dhis.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -76,8 +50,6 @@
public class MonitoringTask
implements Runnable
{
- private static final Log log = LogFactory.getLog( MonitoringTask.class );
-
@Autowired
private ValidationRuleService validationRuleService;
@@ -123,7 +95,7 @@
try
{
- scheduledRun();
+ validationRuleService.scheduledRun();
notifier.notify( taskId, INFO, "Monitoring process done", true );
}
@@ -136,319 +108,4 @@
throw ex;
}
}
-
- // -------------------------------------------------------------------------
- // Support methods
- // -------------------------------------------------------------------------
-
- /**
- * Evaluates all the validation rules that could generate alerts,
- * and sends results (if any) to users who should be notified.
- */
- private void scheduledRun()
- {
- // Find all the rules belonging to groups that will send alerts to user roles.
-
- Set<ValidationRule> rules = getAlertRules();
-
- Collection<OrganisationUnit> sources = organisationUnitService.getAllOrganisationUnits();
- Set<Period> 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 thisScheduledRun = new Date();
-
- log.info( "Scheduled monitoring run sources[" + sources.size() + "] periods[" + periods.size() + "] rules[" + rules.size()
- + "] last run " + lastScheduledRun == null ? "(none)" : lastScheduledRun );
-
- Collection<ValidationResult> results = Validator.validate( sources, periods, rules, ValidationRunType.SCHEDULED,
- lastScheduledRun, constantService, expressionService, periodService, dataValueService );
-
- log.trace( "scheduledRun() results[" + results.size() + "]" );
-
- if ( !results.isEmpty() )
- {
- postAlerts( results, thisScheduledRun );
- }
-
- systemSettingManager.saveSystemSetting( SystemSettingManager.KEY_LAST_MONITORING_RUN, thisScheduledRun );
- }
-
- /**
- * Gets all the validation rules that could generate alerts.
- *
- * @return rules that will generate alerts
- */
- private Set<ValidationRule> getAlertRules()
- {
- Set<ValidationRule> rules = new HashSet<ValidationRule>();
-
- for ( ValidationRuleGroup validationRuleGroup : validationRuleService.getAllValidationRuleGroups() )
- {
- Set<UserAuthorityGroup> userRolesToAlert = validationRuleGroup.getUserAuthorityGroupsToAlert();
-
- if ( userRolesToAlert != null && !userRolesToAlert.isEmpty() )
- {
- 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
- * add some additional periods of type DailyPeriodType not to miss any
- * alerts.
- *
- * @param rules the ValidationRules to be evaluated on this run
- * @return periods to search for new alerts
- */
- private Set<Period> getAlertPeriodsFromRules( Set<ValidationRule> rules )
- {
- Set<Period> periods = new HashSet<Period>();
-
- Set<PeriodType> rulePeriodTypes = getPeriodTypesFromRules( rules );
-
- for ( PeriodType periodType : rulePeriodTypes )
- {
- CalendarPeriodType calendarPeriodType = ( CalendarPeriodType ) periodType;
- Period currentPeriod = calendarPeriodType.createPeriod();
- Period previousPeriod = calendarPeriodType.getPreviousPeriod( currentPeriod );
- periods.addAll( periodService.getIntersectingPeriodsByPeriodType( periodType,
- previousPeriod.getStartDate(), currentPeriod.getEndDate() ) );
- }
-
- return periods;
- }
-
- /**
- * 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<PeriodType> getPeriodTypesFromRules ( Collection<ValidationRule> rules )
- {
- Set<PeriodType> rulePeriodTypes = new HashSet<PeriodType>();
-
- for ( ValidationRule rule : rules )
- {
- rulePeriodTypes.add( periodService.getPeriodTypeByName( rule.getPeriodType().getName() ) );
- }
-
- return rulePeriodTypes;
- }
-
- /**
- * 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
- */
- private void postAlerts( Collection<ValidationResult> validationResults, Date scheduledRunStart )
- {
- SortedSet<ValidationResult> results = new TreeSet<ValidationResult>( validationResults );
-
- Map<List<ValidationResult>, Set<User>> messageMap = getMessageMap( results );
-
- for ( Map.Entry<List<ValidationResult>, Set<User>> entry : messageMap.entrySet() )
- {
- sendAlertmessage( entry.getKey(), entry.getValue(), scheduledRunStart );
- }
- }
-
- /**
- * 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
- * @return map of result sets to users
- */
- private Map<List<ValidationResult>, Set<User>> getMessageMap( Set<ValidationResult> results )
- {
- Map<User, Set<ValidationRule>> userRulesMap = getUserRulesMap();
-
- Map<List<ValidationResult>, Set<User>> messageMap = new HashMap<List<ValidationResult>, Set<User>>();
-
- for ( User user : userRulesMap.keySet() )
- {
- // For users receiving alerts, find the subset of results from run.
-
- Collection<ValidationRule> userRules = userRulesMap.get( user );
- List<ValidationResult> userResults = new ArrayList<ValidationResult>();
-
- for ( ValidationResult result : results )
- {
- if ( userRules.contains( result.getValidationRule() ) )
- {
- userResults.add( result );
- }
- }
-
- // Group this user with other users having the same result subset.
-
- if ( !userResults.isEmpty() )
- {
- Set<User> messageReceivers = messageMap.get( userResults );
- if ( messageReceivers == null )
- {
- messageReceivers = new HashSet<User>();
- messageMap.put( userResults, messageReceivers );
- }
- messageReceivers.add( user );
- }
- }
-
- return messageMap;
- }
-
- /**
- * Constructs a Map where the key is each user who is configured to
- * receive alerts, and the value is a list of rules they should receive
- * results for.
- *
- * @return Map from users to sets of rules
- */
- private Map<User, Set<ValidationRule>> getUserRulesMap()
- {
- Map<User, Set<ValidationRule>> userRulesMap = new HashMap<User, Set<ValidationRule>>();
-
- for ( ValidationRuleGroup validationRuleGroup : validationRuleService.getAllValidationRuleGroups() )
- {
- Collection<UserAuthorityGroup> userRolesToAlert = validationRuleGroup.getUserAuthorityGroupsToAlert();
-
- if ( userRolesToAlert != null && !userRolesToAlert.isEmpty() )
- {
- for ( UserAuthorityGroup role : userRolesToAlert )
- {
- for ( UserCredentials userCredentials : role.getMembers() )
- {
- User user = userCredentials.getUser();
- Set<ValidationRule> userRules = userRulesMap.get( user );
- if ( userRules == null )
- {
- userRules = new HashSet<ValidationRule>();
- userRulesMap.put( user, userRules );
- }
- userRules.addAll( validationRuleGroup.getMembers() );
- }
- }
- }
- }
-
- return userRulesMap;
- }
-
- /**
- * 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 scheduledRunStart date/time when the scheduled run started
- */
- private void sendAlertmessage( List<ValidationResult> results, Set<User> users, Date scheduledRunStart )
- {
- StringBuilder messageBuilder = new StringBuilder();
-
- SimpleDateFormat dateTimeFormatter = new SimpleDateFormat( "yyyy-MM-dd HH:mm" );
-
- Map<String, Integer> importanceCountMap = countResultsByImportanceType( results );
-
- String subject = "DHIS alerts as of " + dateTimeFormatter.format( scheduledRunStart ) + " - priority 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" ) );
-
- messageBuilder
- .append( "<html>" )
- .append( "<head>" ).append( "</head>" )
- .append( "<body>" ).append( subject ).append( "<br />" )
- .append( "<table>" )
- .append( "<tr>" )
- .append( "<th>Organisation Unit</th>" )
- .append( "<th>Period</th>" )
- .append( "<th>Importance</th>" )
- .append( "<th>Left side description</th>" )
- .append( "<th>Value</th>" )
- .append( "<th>Operator</th>" )
- .append( "<th>Value</th>" )
- .append( "<th>Right side description</th>" )
- .append( "</tr>" );
-
- for ( ValidationResult result : results )
- {
- ValidationRule rule = result.getValidationRule();
-
- messageBuilder
- .append( "<tr>" )
- .append( "<td>" ).append( result.getSource().getName() ).append( "<\td>" )
- .append( "<td>" ).append( result.getPeriod().getName() ).append( "<\td>" )
- .append( "<td>" ).append( rule.getImportance() ).append( "<\td>" )
- .append( "<td>" ).append( rule.getLeftSide().getDescription() ).append( "<\td>" )
- .append( "<td>" ).append( result.getLeftsideValue() ).append( "<\td>" )
- .append( "<td>" ).append( rule.getOperator().toString() ).append( "<\td>" )
- .append( "<td>" ).append( result.getRightsideValue() ).append( "<\td>" )
- .append( "<td>" ).append( rule.getRightSide().getDescription() ).append( "<\td>" )
- .append( "</tr>" );
- }
-
- messageBuilder
- .append( "</table>" )
- .append( "</body>" )
- .append( "</html>" );
-
- String messageText = messageBuilder.toString();
-
- log.info( "Alerting users[" + users.size() + "] subject " + subject );
- messageService.sendMessage( subject, messageText, null, users );
- }
-
- /**
- * 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<String, Integer> countResultsByImportanceType ( List<ValidationResult> results )
- {
- Map<String, Integer> importanceCountMap = new HashMap<String, Integer>();
-
- for ( ValidationResult result : results )
- {
- Integer importanceCount = importanceCountMap.get( result.getValidationRule().getImportance() );
-
- importanceCountMap.put( result.getValidationRule().getImportance(), importanceCount == null ? 1
- : importanceCount + 1 );
- }
-
- return importanceCountMap;
- }
}
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2013-10-16 11:58:17 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2013-10-23 22:03:17 +0000
@@ -436,6 +436,9 @@
<property name="constantService" ref="org.hisp.dhis.constant.ConstantService" />
<property name="dataValueService" ref="org.hisp.dhis.datavalue.DataValueService" />
<property name="i18nService" ref="org.hisp.dhis.i18n.I18nService" />
+ <property name="messageService" ref="org.hisp.dhis.message.MessageService" />
+ <property name="organisationUnitService" ref="org.hisp.dhis.organisationunit.OrganisationUnitService" />
+ <property name="systemSettingManager" ref="org.hisp.dhis.setting.SystemSettingManager" />
</bean>
<!-- Scheduled task for data monitoring -->
=== modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/ResourceTableController.java'
--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/ResourceTableController.java 2013-09-30 19:54:38 +0000
+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/ResourceTableController.java 2013-10-23 22:03:17 +0000
@@ -39,6 +39,7 @@
import org.hisp.dhis.scheduling.TaskId;
import org.hisp.dhis.system.scheduling.Scheduler;
import org.hisp.dhis.user.CurrentUserService;
+import org.hisp.dhis.validation.scheduling.MonitoringTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
@@ -64,6 +65,9 @@
private ResourceTableTask resourceTableTask;
@Autowired
+ private MonitoringTask monitoringTask;
+
+ @Autowired
private Scheduler scheduler;
@Autowired
@@ -103,4 +107,15 @@
ContextUtils.okResponse( response, "Initiated resource table update" );
}
+
+ @RequestMapping( value = "/monitoring", method = { RequestMethod.PUT, RequestMethod.POST } )
+ @PreAuthorize( "hasRole('ALL') or hasRole('F_PERFORM_MAINTENANCE')" )
+ public void monitoring( HttpServletResponse response )
+ {
+ monitoringTask.setTaskId( new TaskId( TaskCategory.MONITORING, currentUserService.getCurrentUser() ) );
+
+ scheduler.executeTask( monitoringTask );
+
+ ContextUtils.okResponse( response, "Initiated data monitoring" );
+ }
}