package com.linkedin.thirdeye.anomaly.views;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.linkedin.thirdeye.dashboard.views.TimeBucket;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* We face a problem that if we store the timelines view using AnomalyTimelinesView. The DB has overflow exception when
* storing merged anomalies. As TimeBucket is not space efficient for storage. To solve the problem, we introduce a
* condensed version of AnomalyTimelinesView.
*/
public class CondensedAnomalyTimelinesView {
private static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
Long bucketMillis = 0l; // the bucket size of current time series
List<Long> timeStamps = new ArrayList<>(); // the start time of data points
Map<String, String> summary = new HashMap<>(); // the summary of the view
List<Double> currentValues = new ArrayList<>(); // the observed values
List<Double> baselineValues = new ArrayList<>(); // the expected values
public Long getBucketMillis() {
return bucketMillis;
}
public void setBucketMillis(Long bucketMillis) {
this.bucketMillis = bucketMillis;
}
public List<Long> getTimeStamps() {
return timeStamps;
}
public void addTimeStamps(Long timeStamp) {
this.timeStamps.add(timeStamp);
}
public Map<String, String> getSummary() {
return summary;
}
public void addSummary(String key, String value) {
this.summary.put(key, value);
}
public List<Double> getCurrentValues() {
return currentValues;
}
public void addCurrentValues(Double currentValue) {
this.currentValues.add(currentValue);
}
public List<Double> getBaselineValues() {
return baselineValues;
}
public void addBaselineValues(Double baselineValue) {
this.baselineValues.add(baselineValue);
}
/**
* Convert current instance to an AnomalyTimelinesView instance
* @return
*/
public AnomalyTimelinesView toAnomalyTimelinesView() {
AnomalyTimelinesView anomalyTimelinesView = new AnomalyTimelinesView();
for (int i = 0; i < timeStamps.size(); i++) {
TimeBucket timeBucket = new TimeBucket(timeStamps.get(i), timeStamps.get(i) + bucketMillis,
timeStamps.get(i), timeStamps.get(i) + bucketMillis);
anomalyTimelinesView.addTimeBuckets(timeBucket);
anomalyTimelinesView.addBaselineValues(baselineValues.get(i));
anomalyTimelinesView.addCurrentValues(currentValues.get(i));
}
for (Map.Entry<String, String> entry : summary.entrySet()) {
anomalyTimelinesView.addSummary(entry.getKey(), entry.getValue());
}
return anomalyTimelinesView;
}
/**
* Convert an AnomalyTimelinesView instance to CondensedAnomalyTimelinesView
* @param anomalyTimelinesView
* @return
*/
public static CondensedAnomalyTimelinesView fromAnomalyTimelinesView(AnomalyTimelinesView anomalyTimelinesView) {
CondensedAnomalyTimelinesView condensedView = new CondensedAnomalyTimelinesView();
for (int i = 0; i < anomalyTimelinesView.getTimeBuckets().size(); i++) {
TimeBucket timeBucket = anomalyTimelinesView.getTimeBuckets().get(i);
condensedView.setBucketMillis(timeBucket.getCurrentEnd() - timeBucket.getCurrentStart());
condensedView.addTimeStamps(timeBucket.getCurrentStart());
condensedView.addCurrentValues(anomalyTimelinesView.getCurrentValues().get(i));
condensedView.addBaselineValues(anomalyTimelinesView.getBaselineValues().get(i));
}
for (Map.Entry<String, String> entry : anomalyTimelinesView.getSummary().entrySet()) {
condensedView.addSummary(entry.getKey(), entry.getValue());
}
return condensedView;
}
/**
* Convert current instance into JSON String using ObjectMapper
*
* NOTE, as long as the getter and setter is implemented, the ObjectMapper constructs the JSON String via
* the getter and setter
* @return
* The JSON String of current instance
* @throws JsonProcessingException
*/
public String toJsonString() throws JsonProcessingException {
String jsonString = OBJECT_MAPPER.writeValueAsString(this);
return jsonString;
}
/**
* Given the JSON String of an AnomalyTimelinesView, return an instance of AnomalyTimelinesView
*
* NOTE, as long as the getter and setter is implemented, the ObjectMapper constructs the JSON String via
* the getter and setter
* @param jsonString
* The JSON String of an instance
* @return
* An instance based on the given JSON String
* @throws IOException
*/
public static CondensedAnomalyTimelinesView fromJsonString(String jsonString) throws IOException {
CondensedAnomalyTimelinesView view = OBJECT_MAPPER.readValue(jsonString, CondensedAnomalyTimelinesView.class);
return view;
}
}