Commit 5560efc0 authored by Matija Obreza's avatar Matija Obreza

Calculate differences in execution runs for a specified date range

parent 8e46e7fd
......@@ -15,10 +15,11 @@
*/
package org.genesys2.server.api.v1;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.SortedMap;
import com.fasterxml.jackson.annotation.JsonView;
import org.apache.commons.lang3.StringUtils;
import org.genesys.blocks.model.JsonViews;
import org.genesys2.server.api.ApiBaseController;
......@@ -28,6 +29,7 @@ import org.genesys2.server.model.kpi.Dimension;
import org.genesys2.server.model.kpi.Execution;
import org.genesys2.server.model.kpi.ExecutionRun;
import org.genesys2.server.model.kpi.KPIParameter;
import org.genesys2.server.model.kpi.Observation;
import org.genesys2.server.service.KPIService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
......@@ -42,6 +44,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.annotations.Api;
@RestController("kpiApi1")
......@@ -160,6 +164,30 @@ public class KPIController {
return run;
}
@GetMapping(value = "/executions/{name}/diff")
public SortedMap<Date, List<Observation>> executionRuns(final @PathVariable String name, @RequestParam(value="days", required = false) final Integer days, @RequestParam(value="from", required = false) @DateTimeFormat(pattern="yyyy-MM-dd") Date from,
@RequestParam(value="to", required = false) @DateTimeFormat(pattern="yyyy-MM-dd") Date to) {
Execution execution = kpiService.getExecution(name);
if (execution == null) {
throw new NotFoundElement("No execution " + name);
}
if (days != null) {
Calendar startDate = Calendar.getInstance();
if (to != null) {
startDate.setTime(to);
} else {
to = startDate.getTime();
}
startDate.add(Calendar.DAY_OF_MONTH, -days);
from = startDate.getTime();
}
return kpiService.calculateRunDiff(execution, from, to);
}
/**
* Details
*/
......
......@@ -18,8 +18,8 @@ package org.genesys2.server.model.kpi;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
......@@ -75,6 +75,15 @@ public class Observation implements EntityId, Serializable {
@Column
private int dimensionCount;
public Observation() {
}
public Observation(List<DimensionKey> dimensions, double value) {
this.dimensions.addAll(dimensions);
this.dimensionCount = dimensions.size();
this.value = value;
}
@PrePersist
void prePersist() {
this.dimensionCount = this.dimensions == null ? 0 : this.dimensions.size();
......@@ -136,7 +145,7 @@ public class Observation implements EntityId, Serializable {
return "value=" + value + (stdDev == null ? "" : " stdDev=" + stdDev) + " D=" + dimensions;
}
public boolean hasDimensionKeys(Set<DimensionKey> otherDks) {
public boolean hasDimensionKeys(Collection<DimensionKey> otherDks) {
if (getDimensions().containsAll(otherDks) && otherDks.containsAll(getDimensions())) {
return true;
}
......
......@@ -20,6 +20,9 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import javax.validation.Valid;
import org.genesys2.server.model.kpi.Dimension;
import org.genesys2.server.model.kpi.Execution;
......@@ -29,8 +32,6 @@ import org.genesys2.server.model.kpi.Observation;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import javax.validation.Valid;
public interface KPIService {
KPIParameter save(KPIParameter parameter);
......@@ -86,5 +87,15 @@ public interface KPIService {
List<Observation> filterObservations(Execution execution, Date date, Map<String, Set<String>> keys);
/**
* Calculate differences in observations starting at 'to' date, comparing observations
* with the earlier runs until since 'from' date.
*
* @param execution the execution
* @param from the date of the earliest run
* @param to the date of the latest run
* @return the sorted map keyed by run date, containing run differences
*/
SortedMap<Date, List<Observation>> calculateRunDiff(Execution execution, Date from, Date to);
}
......@@ -22,6 +22,7 @@ import static org.genesys2.server.model.kpi.QExecutionRun.executionRun;
import static org.genesys2.server.model.kpi.QObservation.observation;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
......@@ -29,6 +30,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
......@@ -41,6 +44,7 @@ import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.genesys2.server.component.security.SecurityUtils;
import org.genesys2.server.exception.InvalidApiUsageException;
import org.genesys2.server.model.UserRole;
import org.genesys2.server.model.kpi.Dimension;
import org.genesys2.server.model.kpi.DimensionKey;
......@@ -72,6 +76,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import com.google.api.client.util.Lists;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
......@@ -668,4 +673,73 @@ public class KPIServiceImpl implements KPIService {
}
return dimension;
}
@Override
public SortedMap<Date, List<Observation>> calculateRunDiff(Execution execution, Date from, Date to) {
if (execution == null) {
throw new InvalidApiUsageException("Execution must be provided.");
}
if (from == null || to == null) {
throw new InvalidApiUsageException("From and to dates must be provided.");
}
if (from.after(to)) {
throw new InvalidApiUsageException("From date must be before to date.");
}
LOG.info("Generating run diff from {} to {}", from, to);
SortedMap<Date, List<Observation>> diffs = new TreeMap<>();
Calendar date = Calendar.getInstance();
date.setTime(to);
ExecutionRun currentRun = findExecutionRunByDate(execution, date.getTime());
if (currentRun == null) {
return diffs;
}
do {
date.add(Calendar.DAY_OF_MONTH, -1);
ExecutionRun earlierRun = findExecutionRunByDate(execution, date.getTime());
if (earlierRun == null) {
LOG.debug("No earlier run");
break;
}
List<Observation> diff = Lists.newArrayList();
LOG.debug("Comparing to {}", earlierRun.getTimestamp());
// Additions and changes
for (Observation observation: currentRun.getObservations()) {
Observation earlierObservation = earlierRun.getObservations().stream().filter(o -> o.hasDimensionKeys(observation.getDimensions())).findFirst().orElse(null);
if (earlierObservation == null) {
LOG.debug("No earlier observation for {}", observation.getDimensions());
diff.add(observation);
} else {
LOG.debug("Have earlier observation for {}", observation.getDimensions());
if (observation.getValue() != earlierObservation.getValue()) {
diff.add(new Observation(observation.getDimensions(), observation.getValue() - earlierObservation.getValue()));
}
}
}
// Search for removals
for (Observation earlierObservation: earlierRun.getObservations()) {
Observation observation = currentRun.getObservations().stream().filter(o -> o.hasDimensionKeys(earlierObservation.getDimensions())).findFirst().orElse(null);
if (observation == null) {
LOG.debug("Observation removed for {}", earlierObservation.getDimensions());
diff.add(new Observation(earlierObservation.getDimensions(), -earlierObservation.getValue()));
}
}
if (! diff.isEmpty()) {
diffs.put(currentRun.getTimestamp(), diff);
}
currentRun = earlierRun;
} while (date.getTime().after(from));
return diffs;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment