package net.iponweb.disthene.reader.format;
import com.google.common.base.Joiner;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.*;
import net.iponweb.disthene.reader.beans.TimeSeries;
import net.iponweb.disthene.reader.exceptions.LogarithmicScaleNotAllowed;
import net.iponweb.disthene.reader.graph.DecoratedTimeSeries;
import net.iponweb.disthene.reader.graph.Graph;
import net.iponweb.disthene.reader.graphite.utils.GraphiteUtils;
import net.iponweb.disthene.reader.handler.parameters.RenderParameters;
import org.joda.time.DateTime;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* @author Andrei Ivanov
*/
public class ResponseFormatter {
public static FullHttpResponse formatResponse(List<TimeSeries> timeSeriesList, RenderParameters parameters) throws NotImplementedException, LogarithmicScaleNotAllowed {
// Let's remove empty series
List<TimeSeries> filtered = filterAllNulls(timeSeriesList);
switch (parameters.getFormat()) {
case JSON: return formatResponseAsJson(filtered, parameters);
case RAW: return formatResponseAsRaw(filtered);
case CSV: return formatResponseAsCSV(filtered, parameters);
case PNG: return formatResponseAsPng(filtered, parameters);
case GRAPHPLOT_JSON: return formatResponseAsGraphplotJson(filtered, parameters);
default:throw new NotImplementedException();
}
}
private static FullHttpResponse formatResponseAsCSV(List<TimeSeries> timeSeriesList, RenderParameters renderParameters) {
List<String> results = new ArrayList<>();
for(TimeSeries timeSeries : timeSeriesList) {
Double[] values = timeSeries.getValues();
for(int i = 0; i < values.length; i++) {
DateTime dt = new DateTime((timeSeries.getFrom() + i * timeSeries.getStep()) * 1000, renderParameters.getTz());
String stringValue;
if (values[i] == null) {
stringValue = "";
} else {
BigDecimal bigDecimal = BigDecimal.valueOf(values[i]);
if (bigDecimal.precision() > 10) {
bigDecimal = bigDecimal.setScale(bigDecimal.precision() - 1, BigDecimal.ROUND_HALF_UP);
}
stringValue = bigDecimal.stripTrailingZeros().toPlainString();
}
results.add(timeSeries.getName() + "," + dt.toString("YYYY-MM-dd HH:mm:ss") + "," + stringValue);
}
}
String responseString = Joiner.on("\n").join(results);
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(responseString.getBytes()));
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/csv");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
return response;
}
private static FullHttpResponse formatResponseAsRaw(List<TimeSeries> timeSeriesList) {
List<String> results = new ArrayList<>();
for(TimeSeries timeSeries : timeSeriesList) {
List<String> formattedValues = new ArrayList<>();
for (Double value : timeSeries.getValues()) {
if (value == null) {
formattedValues.add("null");
} else {
formattedValues.add(GraphiteUtils.formatDoubleSpecialPlain(value));
}
}
results.add(timeSeries.getName() + "," + timeSeries.getFrom() + "," + timeSeries.getTo() + "," + timeSeries.getStep() + "|" + Joiner.on(",").useForNull("null").join(formattedValues));
}
String responseString = Joiner.on("\n").join(results);
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(responseString.getBytes()));
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
return response;
}
private static FullHttpResponse formatResponseAsJson(List<TimeSeries> timeSeriesList, RenderParameters renderParameters) {
List<String> results = new ArrayList<>();
Gson gson = new Gson();
// consolidate data points
consolidate(timeSeriesList, renderParameters.getMaxDataPoints());
for(TimeSeries timeSeries : timeSeriesList) {
List<String> datapoints = new ArrayList<>();
for(int i = 0; i < timeSeries.getValues().length; i++) {
String stringValue;
if (timeSeries.getValues()[i] == null) {
stringValue = "null";
} else {
stringValue = GraphiteUtils.formatDoubleSpecialPlain(timeSeries.getValues()[i]);
}
datapoints.add("[" + stringValue + ", " + (timeSeries.getFrom() + timeSeries.getStep() * i) + "]");
}
results.add("{\"target\": " + gson.toJson(timeSeries.getName()) + ", \"datapoints\": [" + Joiner.on(", ").join(datapoints) + "]}");
}
String responseString = "[" + Joiner.on(", ").join(results) + "]";
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(responseString.getBytes()));
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/json");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
return response;
}
private static FullHttpResponse formatResponseAsPng(List<TimeSeries> timeSeriesList, RenderParameters renderParameters) throws LogarithmicScaleNotAllowed {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(Graph.getInstance(renderParameters.getImageParameters().getGraphType(), renderParameters, timeSeriesList).drawGraph()));
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "image/png");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
return response;
}
private static FullHttpResponse formatResponseAsGraphplotJson(List<TimeSeries> timeSeriesList, RenderParameters renderParameters) {
List<String> results = new ArrayList<>();
Gson gson = new Gson();
// consolidate data points
consolidate(timeSeriesList, renderParameters.getMaxDataPoints());
for(TimeSeries timeSeries : timeSeriesList) {
List<String> datapoints = new ArrayList<>();
for(int i = 0; i < timeSeries.getValues().length; i++) {
String stringValue;
if (timeSeries.getValues()[i] == null) {
stringValue = "null";
} else {
stringValue = GraphiteUtils.formatDoubleSpecialPlain(timeSeries.getValues()[i]);
}
datapoints.add(stringValue);
}
results.add("{\"start\": " + timeSeries.getFrom() + ", \"step\": " + timeSeries.getStep() + ", \"end\": " + (timeSeries.getFrom() + timeSeries.getStep() * timeSeries.getValues().length) + ", \"name\": \"" + timeSeries.getName() + "\", \"data\": [" + Joiner.on(", ").join(datapoints) + "]}");
}
String responseString = "[" + Joiner.on(", ").join(results) + "]";
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(responseString.getBytes()));
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/json");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
return response;
}
private static void consolidate(List<TimeSeries> timeSeriesList, int maxDataPoints) {
for (TimeSeries ts : timeSeriesList) {
DecoratedTimeSeries dts = new DecoratedTimeSeries(ts);
if (maxDataPoints < ts.getValues().length) {
dts.setValuesPerPoint((int) Math.ceil(ts.getValues().length / (double) maxDataPoints));
} else {
dts.setValuesPerPoint(1);
}
ts.setStep(dts.getValuesPerPoint() * dts.getStep());
ts.setTo(ts.getFrom() + ts.getStep() * dts.getConsolidatedValues().length - 1);
ts.setValues(dts.getConsolidatedValues());
}
}
private static List<TimeSeries> filterAllNulls(List<TimeSeries> timeSeriesList) {
List<TimeSeries> result = new ArrayList<>();
for (TimeSeries ts : timeSeriesList) {
for (Double value : ts.getValues()) {
if (value != null) {
result.add(ts);
break;
}
}
}
return result;
}
}