Commit a68f41de authored by Matija Obreza's avatar Matija Obreza

KPI revisited

parent 54fbae51
......@@ -22,8 +22,27 @@ import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import org.genesys2.server.model.genesys.Parameter;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* BooleanDimension is a filter applied to the query to select matching records
* for calculation of a {@link Parameter} with Boolean filters: true or false.
*
* <p>
* <b>Example:</b> To count only accessions with mlsStat==true, a "trueOnly"
* {@link BooleanDimension} should be configured.
* </p>
*
* <p>
* There are not that many useful combinations of {@link BooleanDimension}. Most
* commonly all three possible values will be used: null, true, and false.
* </p>
*
* @author mobreza
*
*/
@Entity
public class BooleanDimension extends Dimension<Boolean> {
......
......@@ -27,14 +27,27 @@ import javax.persistence.InheritanceType;
import javax.persistence.Table;
import org.genesys2.server.model.VersionedAuditedModel;
import org.genesys2.server.model.genesys.Parameter;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
/**
* Dimension is a filter applied to the query to select matching records for
* calculation of a {@link Parameter}.
*
* <p>
* <b>Example:</b> See {@link BooleanDimension}.
* </p>
*
* @author mobreza
*
* @param <T>
*/
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "className", discriminatorType = DiscriminatorType.STRING, length = 100)
@Table(name = "kpidimension")
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Dimension<T> extends VersionedAuditedModel {
@Column(length = 100, nullable = false)
......
......@@ -16,22 +16,42 @@
package org.genesys2.server.model.kpi;
import java.io.Serializable;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.genesys2.server.model.BusinessModel;
import org.genesys2.server.model.EntityId;
@Cacheable
@Entity
@Table(name = "kpidimensionkey", uniqueConstraints = { @UniqueConstraint(name = "UQ_dimensionkey", columnNames = { "name", "val" }) })
public class DimensionKey extends BusinessModel {
public class DimensionKey implements EntityId, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
@Column(length = 100, nullable = false, updatable = false)
private String name;
@Column(name = "val", length = 100, nullable = false, unique = false)
@Column(name = "val", length = 100, nullable = false, updatable = false)
private String value;
@Override
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
......
......@@ -25,6 +25,21 @@ import javax.persistence.UniqueConstraint;
import org.genesys2.server.model.VersionedAuditedModel;
import org.hibernate.annotations.Type;
/**
* {@link KPIParameter} defines a the basic JPA query for KPI metrics.
*
* <p><b>Example:</b> KPIParameter with name = "accessionCount" would select records from
* "Accession" {@link #entity} without any condition.
* </p>
*
* <p><b>Example:</b> KPIParameter with {@link #name} = "historicAccessions" would select
* records from "Accession" {@link #entity} with {@link #condition} =
* "historic = true".
* </p>
*
* @author mobreza
*
*/
@Entity
@Table(name = "kpiparameter", uniqueConstraints = { @UniqueConstraint(name = "UQ_kpiparameter_name", columnNames = { "name" }) })
public class KPIParameter extends VersionedAuditedModel {
......@@ -78,9 +93,9 @@ public class KPIParameter extends VersionedAuditedModel {
}
public void setDescription(String description) {
this.description=description;
this.description = description;
}
public String getDescription() {
return description;
}
......
......@@ -16,12 +16,16 @@
package org.genesys2.server.model.kpi;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
......@@ -29,7 +33,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import org.genesys2.server.model.BusinessModel;
import org.genesys2.server.model.EntityId;
import com.fasterxml.jackson.annotation.JsonIgnore;
......@@ -38,7 +42,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
*/
@Entity
@Table(name = "kpiobservation")
public class Observation extends BusinessModel {
public class Observation implements EntityId, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
@Column(name = "`value`")
private double value;
......@@ -60,6 +68,15 @@ public class Observation extends BusinessModel {
this.dimensionCount = this.dimensions == null ? 0 : this.dimensions.size();
}
@Override
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public double getValue() {
return value;
}
......
......@@ -17,14 +17,21 @@
package org.genesys2.server.persistence.domain.kpi;
import java.util.List;
import java.util.Map;
import org.genesys2.server.model.kpi.DimensionKey;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface DimensionKeyRepository extends JpaRepository<DimensionKey, Long> {
@Cacheable(unless = "#result == null", value = "hibernate.org.genesys2.server.model.kpi.DimensionKey", key = "'namevalue-' + #name + '-' + #value")
DimensionKey findByNameAndValue(String name, String value);
List<DimensionKey> findByName(String name);
@Query("select dk.value, dk from DimensionKey dk where dk.name = ?1")
List<Object[]> loadMap(String name);
}
......@@ -18,6 +18,7 @@ package org.genesys2.server.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
......@@ -50,7 +51,6 @@ 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;
@Service
......@@ -240,23 +240,49 @@ public class KPIServiceImpl implements KPIService {
executionRun.setTimestamp(new Date());
executionRunRepository.save(executionRun);
Map<String, Map<String, DimensionKey>> runCache = new HashMap<String, Map<String, DimensionKey>>();
for (Observation obs : observations) {
for (DimensionKey dk : obs.getDimensions()) {
if (!runCache.containsKey(dk.getName())) {
LOG.info("Loading map of values for " + dk.getName());
List<Object[]> x = dimensionKeyRepository.loadMap(dk.getName());
Map<String, DimensionKey> bar = new HashMap<String, DimensionKey>();
for (Object[] foo : x) {
bar.put((String) foo[0], (DimensionKey) foo[1]);
}
LOG.info("Map " + dk.getName() + " size=" + bar.size());
runCache.put(dk.getName(), bar);
}
}
}
for (Observation obs : observations) {
obs.setExecutionRun(executionRun);
}
LOG.info("Pushing to DB 1");
observationRepository.save(observations);
for (Observation obs : observations) {
Set<DimensionKey> dims = new HashSet<DimensionKey>();
for (DimensionKey dk : obs.getDimensions()) {
DimensionKey existing = dimensionKeyRepository.findByNameAndValue(dk.getName(), dk.getValue());
if (existing != null) {
try {
DimensionKey existing = runCache.get(dk.getName()).get(dk.getValue());
dims.add(existing);
} else {
} catch (Throwable e) {
LOG.error(e.getMessage());
dims.add(makeDimensionKey(dk));
}
}
obs.setDimensions(dims);
}
LOG.info("Pushing to DB 2");
return observationRepository.save(observations);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Transactional
private DimensionKey makeDimensionKey(DimensionKey dk) {
return dimensionKeyRepository.save(dk);
}
......
......@@ -63,8 +63,7 @@ public class KPIController extends RestController {
* @throws AuthorizationException
*/
@RequestMapping(value = "/parameter/list", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
Map<String, String> listParameters() {
public @ResponseBody Map<String, String> listParameters() {
LOG.info("Listing KPI parameters");
HashMap<String, String> m = new HashMap<>();
for (KPIParameter kpip : kpiService.listParameters()) {
......@@ -80,8 +79,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/parameter/{name:.+}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
KPIParameter getParameter(@PathVariable("name") String name) {
public @ResponseBody KPIParameter getParameter(@PathVariable("name") String name) {
return kpiService.getParameter(name);
}
......@@ -92,8 +90,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/parameter/{name:.+}", method = { RequestMethod.DELETE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
KPIParameter deleteParameter(@PathVariable("name") String name) {
public @ResponseBody KPIParameter deleteParameter(@PathVariable("name") String name) {
return kpiService.delete(kpiService.getParameter(name));
}
......@@ -104,8 +101,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/parameter", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
KPIParameter createParameter(@RequestBody KPIParameter parameter) throws ValidationException {
public @ResponseBody KPIParameter createParameter(@RequestBody KPIParameter parameter) throws ValidationException {
LOG.info("Updating parameter: " + parameter.getName());
final Validator validator = new Validator();
final List<ConstraintViolation> violations = validator.validate(parameter);
......@@ -124,8 +120,7 @@ public class KPIController extends RestController {
* @throws AuthorizationException
*/
@RequestMapping(value = "/dimension/list", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
HashMap<Long, String> listDimensions() {
public @ResponseBody HashMap<Long, String> listDimensions() {
LOG.info("Listing KPI dimensions");
HashMap<Long, String> m = new HashMap<>();
for (Dimension<?> dim : kpiService.listDimensions()) {
......@@ -141,8 +136,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/dimension/{id}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
Dimension<?> getDimension(@PathVariable("id") long id) {
public @ResponseBody Dimension<?> getDimension(@PathVariable("id") long id) {
return kpiService.getDimension(id);
}
......@@ -153,8 +147,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/dimension/{id}", method = { RequestMethod.DELETE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
Dimension<?> deleteDimension(@PathVariable("id") long id) {
public @ResponseBody Dimension<?> deleteDimension(@PathVariable("id") long id) {
return kpiService.delete(kpiService.getDimension(id));
}
......@@ -165,8 +158,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/dimension", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
Dimension<?> updateBooleanDimension(@RequestBody Dimension<?> dimension) throws ValidationException {
public @ResponseBody Dimension<?> updateBooleanDimension(@RequestBody Dimension<?> dimension) throws ValidationException {
LOG.info("Updating dimension: " + dimension.getName());
final Validator validator = new Validator();
final List<ConstraintViolation> violations = validator.validate(dimension);
......@@ -185,8 +177,7 @@ public class KPIController extends RestController {
* @throws AuthorizationException
*/
@RequestMapping(value = "/execution/list", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
HashMap<String, String> listExecution() {
public @ResponseBody HashMap<String, String> listExecution() {
LOG.info("Listing KPI executions");
HashMap<String, String> m = new HashMap<>();
for (Execution exec : kpiService.listExecutions()) {
......@@ -202,8 +193,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/execution/{executionName:.+}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
ExecutionJson getExecution(@PathVariable("executionName") String executionName) {
public @ResponseBody ExecutionJson getExecution(@PathVariable("executionName") String executionName) {
Execution execution = kpiService.getExecution(executionName);
return ExecutionJson.from(execution);
}
......@@ -215,8 +205,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/execution/{executionName:.+}", method = { RequestMethod.DELETE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
ExecutionJson deleteExecution(@PathVariable("executionName") String executionName) {
public @ResponseBody ExecutionJson deleteExecution(@PathVariable("executionName") String executionName) {
return ExecutionJson.from(kpiService.delete(kpiService.getExecution(executionName)));
}
......@@ -229,8 +218,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/observation/{executionName}/", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
List<Observation> listObservations(@PathVariable("executionName") String executionName,
public @ResponseBody 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.getLastExecutionRun(kpiService.getExecution(executionName)), dimensionFilters, new PageRequest(page - 1,
50));
......@@ -245,11 +233,14 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/execution/{executionName}/execute", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
List<Observation> execute(@PathVariable("executionName") String executionName) {
public @ResponseBody List<Observation> execute(@PathVariable("executionName") String executionName) {
Execution execution = kpiService.getExecution(executionName);
LOG.info("Running execute on : " + executionName);
List<Observation> res = kpiService.execute(execution);
return kpiService.save(execution, res);
LOG.info("Saving results: count=" + res.size());
List<Observation> x = kpiService.save(execution, res);
LOG.info("Done saving results.");
return x;
}
/**
......@@ -259,8 +250,7 @@ public class KPIController extends RestController {
* @throws ValidationException
*/
@RequestMapping(value = "/execution", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
ExecutionJson updateExecution(@RequestBody ExecutionJson ej) throws ValidationException {
public @ResponseBody ExecutionJson updateExecution(@RequestBody ExecutionJson ej) throws ValidationException {
LOG.info("Updating execution: " + ej.title);
final Validator validator = new Validator();
final List<ConstraintViolation> violations = validator.validate(ej);
......
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