/**
   * Returns a map where the key is a user and the value is a naturally-sorted list of results they
   * should receive.
   *
   * @param results all the validation run results, in a sorted set
   * @return map of users to results
   */
  private Map<User, SortedSet<ValidationResult>> getUserResults(
      SortedSet<ValidationResult> results) {
    Map<User, SortedSet<ValidationResult>> userResults = new HashMap<>();

    for (ValidationResult result : results) {
      for (ValidationRuleGroup ruleGroup : result.getValidationRule().getGroups()) {
        if (ruleGroup.hasUserGroupsToAlert()) {
          for (UserGroup userGroup : ruleGroup.getUserGroupsToAlert()) {
            for (User user : userGroup.getMembers()) {
              if (!ruleGroup.isAlertByOrgUnits()
                  || canUserAccessSource(user, result.getOrgUnit())) {
                SortedSet<ValidationResult> resultSet = userResults.get(user);

                if (resultSet == null) {
                  resultSet = new TreeSet<>();

                  userResults.put(user, resultSet);
                }

                resultSet.add(result);
              }
            }
          }
        }
      }
    }
    return userResults;
  }
  public ValidationRuleGroup getValidationRuleGroup(int id, boolean i18nValidationRules) {
    ValidationRuleGroup group = getValidationRuleGroup(id);

    if (i18nValidationRules) {
      i18n(i18nService, group.getMembers());
    }

    return group;
  }
  /**
   * Gets all the validation rules that could generate alerts.
   *
   * @return rules that will generate alerts
   */
  private Set<ValidationRule> getAlertRules() {
    Set<ValidationRule> rules = new HashSet<>();

    for (ValidationRuleGroup validationRuleGroup : getAllValidationRuleGroups()) {
      if (validationRuleGroup.hasUserGroupsToAlert()) {
        rules.addAll(validationRuleGroup.getMembers());
      }
    }

    return rules;
  }
  public Collection<ValidationResult> validate(
      Date startDate,
      Date endDate,
      Collection<OrganisationUnit> sources,
      ValidationRuleGroup group) {
    Map<String, Double> constantMap = constantService.getConstantMap();

    Collection<ValidationResult> validationViolations = new HashSet<ValidationResult>();

    Collection<Period> relevantPeriods = periodService.getPeriodsBetweenDates(startDate, endDate);

    for (OrganisationUnit source : sources) {
      Collection<ValidationRule> relevantRules =
          getRelevantValidationRules(source.getDataElementsInDataSets());
      relevantRules.retainAll(group.getMembers());

      Set<DataElement> dataElements = getDataElementsInValidationRules(relevantRules);

      if (!relevantRules.isEmpty()) {
        for (Period period : relevantPeriods) {
          validationViolations.addAll(
              validateInternal(
                  period,
                  source,
                  relevantRules,
                  dataElements,
                  constantMap,
                  validationViolations.size()));
        }
      }
    }

    return validationViolations;
  }
  @Override
  public Collection<ValidationResult> validate(
      Date startDate,
      Date endDate,
      Collection<OrganisationUnit> sources,
      DataElementCategoryOptionCombo attributeCombo,
      ValidationRuleGroup group,
      boolean sendAlerts,
      I18nFormat format) {
    log.info(
        "Validate start:"
            + startDate
            + " end: "
            + endDate
            + " sources: "
            + sources.size()
            + " group: "
            + group);

    Collection<Period> periods = periodService.getPeriodsBetweenDates(startDate, endDate);
    Collection<ValidationRule> rules = group != null ? group.getMembers() : getAllValidationRules();

    Collection<ValidationResult> results =
        Validator.validate(
            sources,
            periods,
            rules,
            attributeCombo,
            null,
            constantService,
            expressionService,
            periodService,
            dataValueService,
            dataElementCategoryService,
            userService,
            currentUserService);

    formatPeriods(results, format);

    if (sendAlerts) {
      Set<ValidationResult> resultsToAlert = new HashSet<>(results);
      FilterUtils.filter(resultsToAlert, new ValidationResultToAlertFilter());
      postAlerts(resultsToAlert, new Date());
    }

    return results;
  }