|
|
@@ -2,33 +2,42 @@ package cz.senslog.analyzer.server.handler;
|
|
|
|
|
|
import com.google.gson.Gson;
|
|
|
import com.google.gson.GsonBuilder;
|
|
|
-import com.google.gson.JsonObject;
|
|
|
import com.google.gson.JsonSerializer;
|
|
|
import cz.senslog.analyzer.domain.*;
|
|
|
+import cz.senslog.analyzer.persistence.repository.StatisticsConfigRepository;
|
|
|
import cz.senslog.analyzer.persistence.repository.StatisticsRepository;
|
|
|
-import cz.senslog.analyzer.server.vertx.VertxAbstractHandler;
|
|
|
+import cz.senslog.analyzer.server.vertx.AbstractRestHandler;
|
|
|
import cz.senslog.common.util.TimeRange;
|
|
|
+import cz.senslog.common.util.Tuple;
|
|
|
import io.vertx.core.MultiMap;
|
|
|
import io.vertx.core.http.HttpServerResponse;
|
|
|
-import io.vertx.ext.web.RoutingContext;
|
|
|
+import io.vertx.core.json.JsonArray;
|
|
|
+import io.vertx.core.json.JsonObject;
|
|
|
+import org.apache.logging.log4j.LogManager;
|
|
|
+import org.apache.logging.log4j.Logger;
|
|
|
|
|
|
import javax.inject.Inject;
|
|
|
import java.time.Instant;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
+import java.time.temporal.ChronoUnit;
|
|
|
import java.util.*;
|
|
|
|
|
|
import static cz.senslog.analyzer.domain.AttributeValue.*;
|
|
|
import static cz.senslog.common.http.HttpContentType.APPLICATION_JSON;
|
|
|
import static cz.senslog.common.http.HttpHeader.CONTENT_TYPE;
|
|
|
import static java.time.format.DateTimeFormatter.ofPattern;
|
|
|
+import static java.util.Collections.singletonList;
|
|
|
|
|
|
-public class StatisticsHandler extends VertxAbstractHandler {
|
|
|
+public class StatisticsHandler extends AbstractRestHandler {
|
|
|
|
|
|
- private final StatisticsRepository repository;
|
|
|
+ private static final Logger logger = LogManager.getLogger(StatisticsHandler.class);
|
|
|
+
|
|
|
+ private final StatisticsRepository statisticsRepository;
|
|
|
+ private final StatisticsConfigRepository configRepository;
|
|
|
|
|
|
private static final Gson gson = new GsonBuilder()
|
|
|
.registerTypeAdapter(DoubleStatistics.class, (JsonSerializer<DoubleStatistics>) (src, typeOfSrc, context1) -> {
|
|
|
- JsonObject js = new JsonObject();
|
|
|
+ com.google.gson.JsonObject js = new com.google.gson.JsonObject();
|
|
|
// js.addProperty("time", src.getTimestamp().timeFormat());
|
|
|
// js.addProperty("date", src.getTimestamp().dateFormat());
|
|
|
js.addProperty("timestamp", src.getTimestamp().format());
|
|
|
@@ -41,33 +50,145 @@ public class StatisticsHandler extends VertxAbstractHandler {
|
|
|
.create();
|
|
|
|
|
|
@Inject
|
|
|
- public StatisticsHandler(StatisticsRepository repository) {
|
|
|
- this.repository = repository;
|
|
|
+ public StatisticsHandler(
|
|
|
+ StatisticsRepository statisticsRepository,
|
|
|
+ StatisticsConfigRepository configRepository
|
|
|
+ ) {
|
|
|
+ this.statisticsRepository = statisticsRepository;
|
|
|
+ this.configRepository = configRepository;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
@Override
|
|
|
- public void get(RoutingContext context) throws RuntimeException {
|
|
|
- HttpServerResponse response = context.response();
|
|
|
- MultiMap params = context.request().params();
|
|
|
-
|
|
|
- long groupId = Long.parseLong(params.get("group_id"));
|
|
|
- Timestamp from = Timestamp.parse(params.get("from"));
|
|
|
- Timestamp to = Timestamp.parse(params.get("to"));
|
|
|
- GroupBy groupBy = params.contains("groupBy") ? GroupBy.parse(params.get("groupBy")) : null;
|
|
|
- TimeRange<Instant> timeRange = TimeRange.of(from.toInstant(), to.toInstant());
|
|
|
-
|
|
|
- Object result;
|
|
|
- List<DoubleStatistics> statistics = repository.getByTimeRange(groupId, timeRange);
|
|
|
- result = groupBy == null ? statistics : MergeStatistics.merge(groupBy, statistics);
|
|
|
-
|
|
|
- response.putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
- response.end(gson.toJson(result));
|
|
|
+ public void start() {
|
|
|
+
|
|
|
+ router().get("/analyticsByUnitSensor").handler(ctx -> {
|
|
|
+ logger.info("Handling '{}' with the params '{}'.", ctx.request().path(), ctx.request().params().entries());
|
|
|
+
|
|
|
+ HttpServerResponse response = ctx.response();
|
|
|
+ response.putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+ MultiMap params = ctx.request().params();
|
|
|
+
|
|
|
+ long unitIdParam;
|
|
|
+ Long sensorIdParam;
|
|
|
+ TimeRange<Instant> timeRangeParam;
|
|
|
+ GroupBy groupByParam;
|
|
|
+ Interval intervalParam;
|
|
|
+
|
|
|
+ try {
|
|
|
+ unitIdParam = Long.parseLong(params.get("unit_id"));
|
|
|
+ sensorIdParam = params.contains("sensor_id") ? Long.parseLong(params.get("sensor_id")) : null;
|
|
|
+
|
|
|
+ Timestamp fromParam = Timestamp.parse(params.get("from"));
|
|
|
+ Timestamp toParam = Timestamp.parse(params.get("to"));
|
|
|
+ timeRangeParam = TimeRange.of(fromParam.toInstant(), toParam.toInstant());
|
|
|
+
|
|
|
+ groupByParam = GroupBy.parse(params.get("group_by"));
|
|
|
+ intervalParam = Interval.parse(params.get("interval"));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ response.setStatusCode(404).end(new JsonObject()
|
|
|
+ .put("message", e.getMessage()).encode()); return;
|
|
|
+ }
|
|
|
+
|
|
|
+ long timeDiffSec = timeRangeParam.difference(ChronoUnit.SECONDS);
|
|
|
+ StatisticsConfigRepository.Special specialRepository = configRepository.special();
|
|
|
+ // List<Tuple<GroupId, SensorId>>
|
|
|
+ List<Tuple<Long, Long>> sensorInGroup;
|
|
|
+ if (sensorIdParam != null) {
|
|
|
+ long groupId = specialRepository.getGroupIdByUnitSensor(unitIdParam, sensorIdParam, timeDiffSec);
|
|
|
+ sensorInGroup = singletonList(Tuple.of(groupId, sensorIdParam));
|
|
|
+ } else {
|
|
|
+ sensorInGroup = specialRepository.getGroupsByUnit(unitIdParam, timeDiffSec);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<Long, List<DoubleStatistics>> statisticsBySensor = new HashMap<>(sensorInGroup.size());
|
|
|
+ for (Tuple<Long, Long> groupEntry : sensorInGroup) {
|
|
|
+ long groupId = groupEntry.getItem1();
|
|
|
+ long sensorId = groupEntry.getItem2();
|
|
|
+ List<DoubleStatistics> statistics = statisticsRepository.getByTimeRange(groupId, timeRangeParam);
|
|
|
+ statisticsBySensor.put(sensorId, statistics);
|
|
|
+ }
|
|
|
+
|
|
|
+ JsonObject result = new JsonObject();
|
|
|
+ for (Map.Entry<Long, List<DoubleStatistics>> sensorEntry : statisticsBySensor.entrySet()) {
|
|
|
+ String sensorIdStr = sensorEntry.getKey().toString();
|
|
|
+ List<DoubleStatistics> statistics = sensorEntry.getValue();
|
|
|
+
|
|
|
+ if (groupByParam == null) {
|
|
|
+ DoubleStatistics mergedSt = MergeStatistics.mergeToOne(statistics);
|
|
|
+ if (mergedSt != null) {
|
|
|
+ result.put(sensorIdStr, new JsonObject()
|
|
|
+ .put("min", mergedSt.getMin())
|
|
|
+ .put("max", mergedSt.getMax())
|
|
|
+ .put("avg", mergedSt.getAverage())
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Map<String, List<DoubleStatistics>> grouped = MergeStatistics.mergeByGroup(groupByParam, statistics);
|
|
|
+ if (!grouped.isEmpty()) {
|
|
|
+ JsonObject groupJson = new JsonObject();
|
|
|
+ for (Map.Entry<String, List<DoubleStatistics>> entry : grouped.entrySet()) {
|
|
|
+ JsonArray sensorsSt = new JsonArray();
|
|
|
+ for (DoubleStatistics st : entry.getValue()) {
|
|
|
+ sensorsSt.add(new JsonObject()
|
|
|
+ .put("min", st.getMin())
|
|
|
+ .put("max", st.getMax())
|
|
|
+ .put("avg", st.getAverage())
|
|
|
+ .put("timestamp", st.getTimestamp().format())
|
|
|
+ .put("interval", st.getSource().getInterval())
|
|
|
+ );
|
|
|
+ }
|
|
|
+ groupJson.put(entry.getKey(), sensorsSt);
|
|
|
+ }
|
|
|
+ result.put(sensorIdStr, groupJson);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ response.end(result.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get("/analyticsByUser").handler(ctx -> {
|
|
|
+ HttpServerResponse response = ctx.response();
|
|
|
+ response.putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+ MultiMap params = ctx.request().params();
|
|
|
+
|
|
|
+ long userId = Long.parseLong(params.get("user_id"));
|
|
|
+ Timestamp from = Timestamp.parse(params.get("from"));
|
|
|
+ Timestamp to = Timestamp.parse(params.get("to"));
|
|
|
+ GroupBy groupBy = GroupBy.parse(params.get("group_by"));
|
|
|
+ TimeRange<Instant> timeRange = TimeRange.of(from.toInstant(), to.toInstant());
|
|
|
+
|
|
|
+ response.setStatusCode(501).end(new JsonObject()
|
|
|
+ .put("message", "not implemented yet")
|
|
|
+ .encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get("/analyticsByGroup").handler(ctx -> {
|
|
|
+ HttpServerResponse response = ctx.response();
|
|
|
+ response.putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+ MultiMap params = ctx.request().params();
|
|
|
+
|
|
|
+ long groupId = Long.parseLong(params.get("group_id"));
|
|
|
+ Timestamp from = Timestamp.parse(params.get("from"));
|
|
|
+ Timestamp to = Timestamp.parse(params.get("to"));
|
|
|
+ GroupBy groupBy = GroupBy.parse(params.get("group_by"));
|
|
|
+ TimeRange<Instant> timeRange = TimeRange.of(from.toInstant(), to.toInstant());
|
|
|
+
|
|
|
+ List<DoubleStatistics> statistics = statisticsRepository.getByTimeRange(groupId, timeRange);
|
|
|
+ Object result;
|
|
|
+ if (groupBy == null) {
|
|
|
+ result = MergeStatistics.mergeToOne(statistics);
|
|
|
+ } else {
|
|
|
+ result = MergeStatistics.mergeByGroup(groupBy, statistics);
|
|
|
+ }
|
|
|
+
|
|
|
+ response.end(gson.toJson(result));
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
private static class MergeStatistics {
|
|
|
|
|
|
- public static Map<String, List<DoubleStatistics>> merge(GroupBy groupBy, List<DoubleStatistics> statistics) {
|
|
|
+ public static Map<String, List<DoubleStatistics>> mergeByGroup(GroupBy groupBy, List<DoubleStatistics> statistics) {
|
|
|
Map<String, List<DoubleStatistics>> result = new HashMap<>();
|
|
|
DateTimeFormatter formatter = null;
|
|
|
|
|
|
@@ -84,5 +205,16 @@ public class StatisticsHandler extends VertxAbstractHandler {
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
+
|
|
|
+ public static DoubleStatistics mergeToOne(List<DoubleStatistics> statistics) {
|
|
|
+ if (statistics == null || statistics.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ DoubleStatistics first = statistics.get(0);
|
|
|
+ for (int i = 1; i < statistics.size(); i++) {
|
|
|
+ first.accept(statistics.get(i));
|
|
|
+ }
|
|
|
+ return first;
|
|
|
+ }
|
|
|
}
|
|
|
}
|