/**
  * Available for testing, but anomalies need to be created with a valid anomaly function ID
  * (foreign key constraint).
  */
 public void runAdhocFile(
     String filePath,
     int existingFunctionId,
     String windowStartIsoString,
     String windowEndIsoString)
     throws Exception {
   synchronized (sync) {
     File file = new File(filePath);
     if (!file.exists() || file.isDirectory()) {
       throw new IllegalArgumentException("File does not exist or is a directory: " + file);
     }
     AnomalyFunctionSpec spec = reader.readValue(file, AnomalyFunctionSpec.class);
     spec.setId(existingFunctionId);
     runAdhocConfig(spec, windowStartIsoString, windowEndIsoString, filePath);
   }
 }
  public void runAdHoc(Long id, String windowStartIsoString, String windowEndIsoString)
      throws Exception {
    synchronized (sync) {
      AnomalyFunctionSpec spec = specDAO.findById(id);
      if (spec == null) {
        throw new IllegalArgumentException("No function with id " + id);
      }
      AnomalyFunction anomalyFunction = anomalyFunctionFactory.fromSpec(spec);

      String triggerKey = String.format("ad_hoc_anomaly_function_trigger_%d", spec.getId());
      Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startNow().build();

      String jobKey = String.format("ad_hoc_anomaly_function_job_%d", spec.getId());
      buildAndScheduleJob(
          jobKey, trigger, anomalyFunction, spec, windowStartIsoString, windowEndIsoString);
    }
  }
  private void handleResults(List<AnomalyResult> results) {
    Session session = sessionFactory.openSession();
    try {
      ManagedSessionContext.bind(session);
      Transaction transaction = session.beginTransaction();
      try {
        for (AnomalyResult result : results) {
          // Properties that always come from the function spec
          AnomalyFunctionSpec spec = anomalyFunction.getSpec();
          result.setFunctionId(spec.getId());
          result.setFunctionType(spec.getType());
          result.setFunctionProperties(spec.getProperties());
          result.setCollection(spec.getCollection());
          result.setMetric(spec.getMetric());
          result.setFilters(spec.getFilters());

          // make sure score and weight are valid numbers
          result.setScore(normalize(result.getScore()));
          result.setWeight(normalize(result.getWeight()));
          resultDAO.createOrUpdate(result);
        }
        transaction.commit();
      } catch (Exception e) {
        transaction.rollback();
        throw new RuntimeException(e);
      }
    } finally {
      session.close();
      ManagedSessionContext.unbind(sessionFactory);
    }
  }
  public void start(Long id) throws Exception {
    synchronized (sync) {
      AnomalyFunctionSpec spec = specDAO.findById(id);
      if (spec == null) {
        throw new IllegalArgumentException("No function with id " + id);
      }
      AnomalyFunction anomalyFunction = anomalyFunctionFactory.fromSpec(spec);

      String triggerKey = String.format("scheduled_anomaly_function_trigger_%d", spec.getId());
      CronTrigger trigger =
          TriggerBuilder.newTrigger()
              .withIdentity(triggerKey)
              .withSchedule(CronScheduleBuilder.cronSchedule(spec.getCron()))
              .build();

      String jobKey = String.format("scheduled_anomaly_function_job_%d", spec.getId());
      scheduledJobKeys.put(id, jobKey);

      buildAndScheduleJob(jobKey, trigger, anomalyFunction, spec, null, null); // use schedule time
      // to determine
      // start/end
    }
  }
  public List<TaskResult> execute(TaskInfo taskInfo, TaskContext taskContext) throws Exception {

    List<TaskResult> taskResult = new ArrayList<>();
    LOG.info("Begin executing task {}", taskInfo);
    resultDAO = taskContext.getResultDAO();
    relationDAO = taskContext.getRelationDAO();
    sessionFactory = taskContext.getSessionFactory();
    anomalyFunctionFactory = taskContext.getAnomalyFunctionFactory();

    anomalyFunctionSpec = taskInfo.getAnomalyFunctionSpec();
    anomalyFunction = anomalyFunctionFactory.fromSpec(anomalyFunctionSpec);
    windowStart = taskInfo.getWindowStartTime();
    windowEnd = taskInfo.getWindowEndTime();

    // Compute metric function
    TimeGranularity timeGranularity =
        new TimeGranularity(
            anomalyFunctionSpec.getBucketSize(), anomalyFunctionSpec.getBucketUnit());
    // TODO put sum into the function config
    metricFunction = new MetricFunction(MetricAggFunction.SUM, anomalyFunctionSpec.getMetric());

    // Collection
    collection = anomalyFunctionSpec.getCollection();

    // Filters
    String filters = anomalyFunctionSpec.getFilters();

    LOG.info(
        "Running anomaly detection job with metricFunction: {}, collection: {}",
        metricFunction,
        collection);

    CollectionSchema collectionSchema = null;
    try {
      collectionSchema = CACHE_REGISTRY_INSTANCE.getCollectionSchemaCache().get(collection);
    } catch (Exception e) {
      LOG.error("Exception when reading collection schema cache", e);
    }
    collectionDimensions = collectionSchema.getDimensionNames();

    // Get existing anomalies for this time range
    knownAnomalies = getExistingAnomalies();

    // Seed request with top-level...
    TimeSeriesRequest topLevelRequest = new TimeSeriesRequest();
    topLevelRequest.setCollectionName(collection);
    List<MetricFunction> metricFunctions = Collections.singletonList(metricFunction);
    List<MetricExpression> metricExpressions = Utils.convertToMetricExpressions(metricFunctions);
    topLevelRequest.setMetricExpressions(metricExpressions);
    topLevelRequest.setAggregationTimeGranularity(timeGranularity);
    topLevelRequest.setStart(windowStart);
    topLevelRequest.setEnd(windowEnd);
    topLevelRequest.setEndDateInclusive(false);
    if (StringUtils.isNotBlank(filters)) {
      topLevelRequest.setFilterSet(ThirdEyeUtils.getFilterSet(filters));
    }
    String exploreDimension = taskInfo.getGroupByDimension();
    if (StringUtils.isNotBlank(exploreDimension)) {
      topLevelRequest.setGroupByDimensions(
          Collections.singletonList(taskInfo.getGroupByDimension()));
    }

    LOG.info(
        "Running anomaly detection job with windowStartProp: {}, windowEndProp: {}, metricExpressions: {}, timeGranularity: {}, windowStart: {}, windowEnd: {}",
        windowStart,
        windowEnd,
        metricExpressions,
        timeGranularity);

    List<AnomalyResult> results = exploreCombination(topLevelRequest);
    LOG.info("{} anomalies found in total", anomalyCounter);

    return taskResult;
  }