Commit 37b12081 authored by Matija Obreza's avatar Matija Obreza
Browse files

Observations link to Executions

Executions have unique names
parent 995cc53a
......@@ -28,11 +28,27 @@ import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import net.sf.oval.constraint.NotBlank;
import org.genesys2.server.model.VersionedAuditedModel;
/**
* Evaluates {@link KPIParameter} by {@link Dimension}s.
*
* @author matijaobreza
*
*/
@Entity
@Table(name = "kpiexecution")
public class Execution extends VersionedAuditedModel {
/**
* This specifies the "key" under which observations are filed
*/
@NotBlank
@Column(length = 100, unique = true, nullable = false)
private String name;
@ManyToOne(cascade = {}, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "parameterId")
private KPIParameter parameter;
......@@ -44,6 +60,14 @@ public class Execution extends VersionedAuditedModel {
@Column(length = 100)
private String title;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setParameter(KPIParameter parameter) {
this.parameter = parameter;
}
......
......@@ -65,6 +65,10 @@ public class NumericListDimension extends FixedListDimension<Number> {
}
private Number toType(Double d) {
if (this.clazz == null) {
getClazz();
}
if (this.clazz == Integer.class) {
return d.intValue();
} else if (this.clazz == Long.class) {
......@@ -78,7 +82,7 @@ public class NumericListDimension extends FixedListDimension<Number> {
} else if (this.clazz == Byte.class) {
return d.byteValue();
}
return null;
throw new RuntimeException("Unsupported NumericListDimension type " + this.javaType + " clazz=" + this.clazz);
}
public Class<?> getClazz() {
......
......@@ -36,6 +36,9 @@ import org.genesys2.server.model.BusinessModel;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* Holds results of {@link Execution} run.
*/
@Entity
@Table(name = "kpiobservation")
public class Observation extends BusinessModel {
......@@ -45,15 +48,12 @@ public class Observation extends BusinessModel {
private Date timestamp = new Date();
@Column(name = "`value`")
private long value;
private double value;
@JsonIgnore
@ManyToOne(cascade = {}, fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "parameterId")
private KPIParameter parameter;
@JsonIgnore
private long executionId;
@JoinColumn(name = "executionId")
private Execution execution;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "kpiobservationdimension", joinColumns = @JoinColumn(name = "observationId"), inverseJoinColumns = @JoinColumn(name = "dimensionKeyId"))
......@@ -67,22 +67,14 @@ public class Observation extends BusinessModel {
this.dimensionCount = this.dimensions == null ? 0 : this.dimensions.size();
}
public long getValue() {
public double getValue() {
return value;
}
public void setValue(long value) {
public void setValue(double value) {
this.value = value;
}
public KPIParameter getParameter() {
return parameter;
}
public void setParameter(KPIParameter parameter) {
this.parameter = parameter;
}
public Set<DimensionKey> getDimensions() {
return dimensions;
}
......@@ -106,17 +98,17 @@ public class Observation extends BusinessModel {
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public long getExecutionId() {
return executionId;
public Execution getExecution() {
return execution;
}
public void setExecutionId(long executionId) {
this.executionId = executionId;
public void setExecution(Execution execution) {
this.execution = execution;
}
@Override
public String toString() {
return parameter.getName() + "=" + value + " D=" + dimensions + " T=" + timestamp.getTime();
return execution.getName() + "=" + value + " D=" + dimensions + " T=" + timestamp.getTime();
}
}
......@@ -21,4 +21,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
public interface ExecutionRepository extends JpaRepository<Execution, Long> {
Execution findByName(String name);
}
......@@ -20,12 +20,14 @@ import java.util.List;
import java.util.Set;
import org.genesys2.server.model.kpi.DimensionKey;
import org.genesys2.server.model.kpi.KPIParameter;
import org.genesys2.server.model.kpi.Execution;
import org.genesys2.server.model.kpi.Observation;
import org.springframework.data.domain.Pageable;
public interface ObservationCustomRepository {
List<Observation> findObservations(KPIParameter parameter, Set<DimensionKey> dks, Pageable page);
List<Observation> findObservations(Execution execution, Set<DimensionKey> dks, Pageable page);
Observation findLastObservation(Execution execution, Set<DimensionKey> dks);
}
......@@ -17,20 +17,24 @@
package org.genesys2.server.persistence.domain.kpi;
import java.util.List;
import java.util.Set;
import org.genesys2.server.model.kpi.DimensionKey;
import org.genesys2.server.model.kpi.KPIParameter;
import org.genesys2.server.model.kpi.Execution;
import org.genesys2.server.model.kpi.Observation;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
public interface ObservationRepository extends JpaRepository<Observation, Long>, ObservationCustomRepository {
List<Observation> findByParameter(KPIParameter parameter, Pageable page);
List<Observation> findByExecution(Execution execution, Pageable page);
@Query("select o from Observation o where o.parameter=?1 and ?2 member of o.dimensions order by o.timestamp desc")
List<Observation> findObservationsByDimensionKey(KPIParameter parameter, DimensionKey next, Pageable page);
@Query("select o from Observation o where o.execution=?1 and ?2 member of o.dimensions order by o.timestamp desc")
List<Observation> listObservationsByDimensionKey(Execution execution, DimensionKey next, Pageable page);
@Modifying
@Query("delete from Observation o where o.execution=?1")
void deleteByExecution(Execution execution);
}
......@@ -26,7 +26,7 @@ import javax.persistence.Query;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.model.kpi.DimensionKey;
import org.genesys2.server.model.kpi.KPIParameter;
import org.genesys2.server.model.kpi.Execution;
import org.genesys2.server.model.kpi.Observation;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
......@@ -40,23 +40,22 @@ public class ObservationRepositoryCustomImpl implements ObservationCustomReposit
@SuppressWarnings("unchecked")
@Override
public List<Observation> findObservations(KPIParameter parameter, Set<DimensionKey> dks, Pageable page) {
LOG.info("Doing magic!");
public List<Observation> findObservations(Execution execution, Set<DimensionKey> dks, Pageable page) {
StringBuilder where = new StringBuilder();
int i = 2;
for (DimensionKey dk : dks) {
i++;
where.append("and ?").append(i).append(" member of o.dimensions ");
final int OFFSET = 3;
for (int i = 0; i < dks.size(); i++) {
where.append("and ?").append(i + OFFSET).append(" member of o.dimensions ");
}
Query q = entityManager.createQuery("select o from Observation o where o.parameter=?1 and o.dimensionCount=?2 " + where.toString() + " order by o.timestamp desc");
q.setParameter(1, parameter);
Query q = entityManager.createQuery("select o from Observation o where o.execution=?1 and o.dimensionCount=?2 " + where.toString()
+ " order by o.timestamp desc");
q.setParameter(1, execution);
q.setParameter(2, dks.size());
i = 2;
int i = OFFSET;
for (DimensionKey dk : dks) {
i++;
q.setParameter(i, dk);
i++;
}
q.setFirstResult(page.getOffset());
......@@ -65,4 +64,25 @@ public class ObservationRepositoryCustomImpl implements ObservationCustomReposit
return q.getResultList();
}
@Override
public Observation findLastObservation(Execution execution, Set<DimensionKey> dks) {
StringBuilder where = new StringBuilder();
final int OFFSET = 3;
for (int i = 0; i < dks.size(); i++) {
where.append("and ?").append(i + OFFSET).append(" member of o.dimensions ");
}
Query q = entityManager.createQuery("select o from Observation o where o.execution=?1 and o.dimensionCount=?2 " + where.toString()
+ " order by o.timestamp desc limit 1", Observation.class);
q.setParameter(1, execution);
q.setParameter(2, dks.size());
int i = OFFSET;
for (DimensionKey dk : dks) {
q.setParameter(i, dk);
i++;
}
return (Observation) q.getSingleResult();
}
}
......@@ -49,14 +49,13 @@ public interface KPIService {
Execution save(Execution execution);
Execution getExecution(long id);
Execution getExecution(String executionName);
Execution delete(Execution execution);
List<Observation> execute(Execution execution);
List<Observation> save(List<Observation> observations);
void delete(List<Observation> observations);
List<Observation> save(Execution execution, List<Observation> observations);
List<KPIParameter> listParameters();
......@@ -64,6 +63,9 @@ public interface KPIService {
List<Execution> listExecutions();
List<Observation> listObservations(KPIParameter parameter, Map<String, String> dimensionFilters, Pageable page);
List<Observation> listObservations(Execution execution, Map<String, String> dimensionFilters, Pageable page);
void deleteObservations(Execution execution);
}
......@@ -44,6 +44,7 @@ import org.genesys2.server.service.KPIService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
......@@ -71,6 +72,7 @@ public class KPIServiceImpl implements KPIService {
@Autowired
private DimensionKeyRepository dimensionKeyRepository;
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#parameter, 'ADMINISTRATION')")
@Override
@Transactional
public KPIParameter save(KPIParameter parameter) {
......@@ -92,6 +94,7 @@ public class KPIServiceImpl implements KPIService {
return parameterRepository.findAll(new Sort("name"));
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#parameter, 'ADMINISTRATION')")
@Override
@Transactional
public KPIParameter delete(KPIParameter parameter) {
......@@ -100,6 +103,7 @@ public class KPIServiceImpl implements KPIService {
return parameter;
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#dimension, 'ADMINISTRATION')")
@Override
@Transactional
public Dimension<?> save(Dimension<?> dimension) {
......@@ -107,6 +111,7 @@ public class KPIServiceImpl implements KPIService {
return dimensionRepository.save(dimension);
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#dimension, 'ADMINISTRATION')")
@Override
@Transactional
public Dimension<?> delete(Dimension<?> dimension) {
......@@ -133,6 +138,12 @@ public class KPIServiceImpl implements KPIService {
return executionRepository.findOne(id);
}
@Override
public Execution getExecution(String executionName) {
return executionRepository.findByName(executionName);
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#execution, 'ADMINISTRATION')")
@Override
@Transactional
public Execution save(Execution execution) {
......@@ -144,14 +155,23 @@ public class KPIServiceImpl implements KPIService {
return executionRepository.findAll();
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#execution, 'ADMINISTRATION')")
@Override
@Transactional
public Execution delete(Execution execution) {
// Execption thrown if there are Observations (which is okay).
executionRepository.delete(execution);
execution.setId(null);
return execution;
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#execution, 'ADMINISTRATION')")
@Override
@Transactional
public void deleteObservations(Execution execution) {
observationRepository.deleteByExecution(execution);
}
@Override
public Set<?> getValues(Dimension<?> dim) {
if (dim instanceof JpaDimension) {
......@@ -188,6 +208,7 @@ public class KPIServiceImpl implements KPIService {
}
// readonly mode
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#execution, 'ADMINISTRATION')")
@Override
public List<Observation> execute(Execution execution) {
List<Observation> results = new ArrayList<Observation>();
......@@ -196,10 +217,12 @@ public class KPIServiceImpl implements KPIService {
return results;
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#execution, 'ADMINISTRATION')")
@Override
@Transactional
public List<Observation> save(List<Observation> observations) {
public List<Observation> save(Execution execution, List<Observation> observations) {
for (Observation obs : observations) {
obs.setExecution(execution);
Set<DimensionKey> dims = new HashSet<DimensionKey>();
for (DimensionKey dk : obs.getDimensions()) {
DimensionKey existing = dimensionKeyRepository.findByNameAndValue(dk.getName(), dk.getValue());
......@@ -219,17 +242,12 @@ public class KPIServiceImpl implements KPIService {
return dimensionKeyRepository.save(dk);
}
@Override
@Transactional
public void delete(List<Observation> observations) {
observationRepository.delete(observations);
}
private void internalExecute(String paQuery, Execution paramExec, List<Observation> results, int depth, List<Object> params) {
Dimension<?> dim = paramExec.getDimension(depth);
if (dim == null) {
// execute
LOG.info("Executing: " + paQuery + " params=" + params);
if (LOG.isDebugEnabled())
LOG.debug("Executing: " + paQuery + " params=" + params);
long res = getSingleResult(paQuery, params.toArray());
printRes(res, paramExec, params.toArray(), results);
} else {
......@@ -245,14 +263,15 @@ public class KPIServiceImpl implements KPIService {
}
private void printRes(long res, Execution paramExec, Object[] array, List<Observation> results) {
LOG.info("Reporting result\n\n");
KPIParameter parameter = paramExec.getParameter();
LOG.info(parameter.getName() + "=" + res);
if (LOG.isDebugEnabled()) {
LOG.debug("Reporting result\n\n");
LOG.debug(parameter.getName() + "=" + res);
}
Observation observation = new Observation();
observation.setParameter(parameter);
observation.setValue(res);
observation.setExecutionId(paramExec.getId());
observation.setExecution(paramExec);
for (int i = 0; i < array.length; i++) {
String name = paramExec.getDimension(i).getName();
String value = array[i].toString();
......@@ -262,23 +281,27 @@ public class KPIServiceImpl implements KPIService {
dk.setName(paramExec.getDimension(i).getName());
dk.setValue(array[i].toString());
}
LOG.info("\t\t" + dk);
if (LOG.isDebugEnabled())
LOG.debug("\t\t" + dk);
observation.getDimensions().add(dk);
}
LOG.info("OBSERVATION: " + observation);
if (LOG.isDebugEnabled())
LOG.debug("OBSERVATION: " + observation);
results.add(observation);
}
@Override
public List<Observation> listObservations(KPIParameter parameter, Map<String, String> dimensionFilters, Pageable page) {
public List<Observation> listObservations(Execution execution, Map<String, String> dimensionFilters, Pageable page) {
List<Observation> res = null;
if (dimensionFilters == null || dimensionFilters.isEmpty()) {
LOG.debug("Dimension filters not provided");
res = observationRepository.findByParameter(parameter, page);
res = observationRepository.findByExecution(execution, page);
} else {
Set<DimensionKey> dks = getDimensionKeys(dimensionFilters);
LOG.info("Got " + dks.size() + " dimension keys.");
res = observationRepository.findObservations(parameter, dks, page);
LOG.debug("Got " + dks.size() + " dimension keys.");
res = observationRepository.findObservations(execution, dks, page);
}
// Load lazy
......
......@@ -186,11 +186,11 @@ public class KPIController extends RestController {
*/
@RequestMapping(value = "/execution/list", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
HashMap<Long, String> listExecution() {
HashMap<String, String> listExecution() {
LOG.info("Listing KPI executions");
HashMap<Long, String> m = new HashMap<>();
HashMap<String, String> m = new HashMap<>();
for (Execution exec : kpiService.listExecutions()) {
m.put(exec.getId(), exec.getTitle());
m.put(exec.getName(), exec.getTitle());
}
return m;
}
......@@ -201,10 +201,10 @@ public class KPIController extends RestController {
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/execution/{id}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
@RequestMapping(value = "/execution/{executionName:.+}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
ExecutionJson getExecution(@PathVariable("id") long id) {
Execution execution = kpiService.getExecution(id);
ExecutionJson getExecution(@PathVariable("executionName") String executionName) {
Execution execution = kpiService.getExecution(executionName);
return ExecutionJson.from(execution);
}
......@@ -214,10 +214,10 @@ public class KPIController extends RestController {
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/execution/{id}", method = { RequestMethod.DELETE }, produces = { MediaType.APPLICATION_JSON_VALUE })
@RequestMapping(value = "/execution/{executionName:.+}", method = { RequestMethod.DELETE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
ExecutionJson deleteExecution(@PathVariable("id") long id) {
return ExecutionJson.from(kpiService.delete(kpiService.getExecution(id)));
ExecutionJson deleteExecution(@PathVariable("executionName") String executionName) {
return ExecutionJson.from(kpiService.delete(kpiService.getExecution(executionName)));
}
/**
......@@ -228,11 +228,11 @@ public class KPIController extends RestController {
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/observation/{parameterName}/", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
@RequestMapping(value = "/observation/{executionName}/", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
List<Observation> listObservations(@PathVariable("parameterName") String parameterName,
List<Observation> listObservations(@PathVariable("executionName") String executionName,
@RequestParam(value = "page", required = false, defaultValue = "1") int page, @RequestBody(required = false) Map<String, String> dimensionFilters) {
return kpiService.listObservations(kpiService.getParameter(parameterName), dimensionFilters, new PageRequest(page - 1, 50));
return kpiService.listObservations(kpiService.getExecution(executionName), dimensionFilters, new PageRequest(page - 1, 50));
}
/**
......@@ -243,12 +243,12 @@ public class KPIController extends RestController {
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/execution/{id}/execute", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
@RequestMapping(value = "/execution/{executionName}/execute", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
List<Observation> execute(@PathVariable("id") long id) {
Execution execution = kpiService.getExecution(id);
List<Observation> execute(@PathVariable("executionName") String executionName) {
Execution execution = kpiService.getExecution(executionName);
List<Observation> res = kpiService.execute(execution);
return kpiService.save(res);
return kpiService.save(execution, res);
}
/**
......@@ -273,6 +273,7 @@ public class KPIController extends RestController {
execution = kpiService.getExecution(ej.id);
if (execution == null)
execution = new Execution();
execution.setName(ej.name);
execution.setTitle(ej.title);
execution.setParameter(kpiService.getParameter(ej.parameter));
execution.setVersion(ej.version);
......
......@@ -24,6 +24,7 @@ import org.genesys2.server.model.kpi.Execution;
public class ExecutionJson {
public Long id;
public String name;
public long version = 0;
public String parameter;
public String title;
......@@ -34,6 +35,7 @@ public class ExecutionJson {
return null;
ExecutionJson ej = new ExecutionJson();
ej.id = execution.getId();
ej.name = execution.getName();
ej.version = execution.getVersion();
ej.parameter = execution.getParameter().getName();
ej.title = execution.getTitle();
......
......@@ -203,7 +203,7 @@ public class KPIEntitiesTest {
KPIParameter parameter = new KPIParameter();
parameter.setTitle("KPI Dimensions");
parameter.setDescription("Total number of KPI Dimensions defined");
parameter.setName("dimension.count");
parameter.setName("dimension.count." + System.currentTimeMillis());
parameter.setEntity("Dimension");
KPIParameter persisted = kpiService.save(parameter);
......@@ -256,6 +256,7 @@ public class KPIEntitiesTest {
kpiService.save(dimVersion);
Execution execution = new Execution();
execution.setName("foobar");