Commit 0a7f58a4 authored by Matija Obreza's avatar Matija Obreza
Browse files

REST API for indicators

parent bba5dc48
...@@ -12,10 +12,13 @@ import javax.persistence.Table; ...@@ -12,10 +12,13 @@ import javax.persistence.Table;
import org.genesys2.server.model.VersionedAuditedModel; import org.genesys2.server.model.VersionedAuditedModel;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@Entity @Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "className", discriminatorType = DiscriminatorType.STRING, length = 100) @DiscriminatorColumn(name = "className", discriminatorType = DiscriminatorType.STRING, length = 100)
@Table(name = "kpidimension") @Table(name = "kpidimension")
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
public abstract class Dimension<T> extends VersionedAuditedModel { public abstract class Dimension<T> extends VersionedAuditedModel {
@Column(length = 100, nullable = false) @Column(length = 100, nullable = false)
......
...@@ -4,6 +4,7 @@ import java.util.ArrayList; ...@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
...@@ -20,10 +21,13 @@ public class Execution extends VersionedAuditedModel { ...@@ -20,10 +21,13 @@ public class Execution extends VersionedAuditedModel {
@JoinColumn(name = "parameterId") @JoinColumn(name = "parameterId")
private KPIParameter parameter; private KPIParameter parameter;
@OneToMany(orphanRemoval=true, fetch = FetchType.EAGER, cascade = { CascadeType.ALL }) @OneToMany(orphanRemoval = true, fetch = FetchType.EAGER, cascade = { CascadeType.ALL })
@JoinColumn(name = "executionId") @JoinColumn(name = "executionId")
private List<ExecutionDimension> dimensions = new ArrayList<ExecutionDimension>(); private List<ExecutionDimension> dimensions = new ArrayList<ExecutionDimension>();
@Column(length = 100)
private String title;
public void setParameter(KPIParameter parameter) { public void setParameter(KPIParameter parameter) {
this.parameter = parameter; this.parameter = parameter;
} }
...@@ -82,7 +86,7 @@ public class Execution extends VersionedAuditedModel { ...@@ -82,7 +86,7 @@ public class Execution extends VersionedAuditedModel {
return sb.toString(); return sb.toString();
} }
public List<ExecutionDimension> getExecutionDimensions() { public List<ExecutionDimension> getExecutionDimensions() {
return dimensions; return dimensions;
} }
...@@ -92,4 +96,12 @@ public class Execution extends VersionedAuditedModel { ...@@ -92,4 +96,12 @@ public class Execution extends VersionedAuditedModel {
return null; return null;
return dimensions.get(depth).getDimension(); return dimensions.get(depth).getDimension();
} }
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
} }
...@@ -5,6 +5,8 @@ import java.util.Set; ...@@ -5,6 +5,8 @@ import java.util.Set;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity @Entity
public class JpaDimension extends Dimension<Object> { public class JpaDimension extends Dimension<Object> {
...@@ -12,7 +14,10 @@ public class JpaDimension extends Dimension<Object> { ...@@ -12,7 +14,10 @@ public class JpaDimension extends Dimension<Object> {
private String entity; private String entity;
@Column(length = 50) @Column(length = 50)
private String field; private String field;
@Column(name = "`condition`", length = 100)
private String condition;
@JsonIgnore
@Override @Override
public Set<Object> getValues() { public Set<Object> getValues() {
throw new RuntimeException("JpaDimensions must be evaluated in KPIService"); throw new RuntimeException("JpaDimensions must be evaluated in KPIService");
...@@ -34,4 +39,12 @@ public class JpaDimension extends Dimension<Object> { ...@@ -34,4 +39,12 @@ public class JpaDimension extends Dimension<Object> {
this.field = field; this.field = field;
} }
public void setCondition(String condition) {
this.condition = condition;
}
public String getCondition() {
return condition;
}
} }
...@@ -21,7 +21,7 @@ public class KPIParameter extends VersionedAuditedModel { ...@@ -21,7 +21,7 @@ public class KPIParameter extends VersionedAuditedModel {
@Column(length = 100, nullable = false) @Column(length = 100, nullable = false)
private String entity; private String entity;
@Column(name = "`condition`", length = 100) @Column(name = "`condition`", length = 300)
private String condition; private String condition;
@Lob @Lob
......
...@@ -25,15 +25,22 @@ public class NumericListDimension extends FixedListDimension<Number> { ...@@ -25,15 +25,22 @@ public class NumericListDimension extends FixedListDimension<Number> {
private Set<Double> values; private Set<Double> values;
public void setValues(Set<Number> list) { public void setValues(Set<Number> list) {
Set<Double> doubles = new HashSet<Double>(); if (list == null) {
for (Number n : list) { this.values = null;
doubles.add(n.doubleValue()); } else {
Set<Double> doubles = new HashSet<Double>();
for (Number n : list) {
doubles.add(n.doubleValue());
}
this.values = doubles;
} }
this.values = doubles;
} }
@Override @Override
public Set<Number> getValues() { public Set<Number> getValues() {
if (values == null) {
return null;
}
Set<Number> numbers = new HashSet<Number>(); Set<Number> numbers = new HashSet<Number>();
for (double d : values) { for (double d : values) {
numbers.add(toType(d)); numbers.add(toType(d));
......
...@@ -18,6 +18,8 @@ import javax.persistence.TemporalType; ...@@ -18,6 +18,8 @@ import javax.persistence.TemporalType;
import org.genesys2.server.model.BusinessModel; import org.genesys2.server.model.BusinessModel;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity @Entity
@Table(name = "kpiobservation") @Table(name = "kpiobservation")
public class Observation extends BusinessModel { public class Observation extends BusinessModel {
...@@ -29,12 +31,13 @@ public class Observation extends BusinessModel { ...@@ -29,12 +31,13 @@ public class Observation extends BusinessModel {
@Column(name = "`value`") @Column(name = "`value`")
private long value; private long value;
@JsonIgnore
@ManyToOne(cascade = {}, fetch = FetchType.LAZY, optional = false) @ManyToOne(cascade = {}, fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "parameterId") @JoinColumn(name = "parameterId")
private KPIParameter parameter; private KPIParameter parameter;
@ManyToMany(fetch = FetchType.LAZY) @ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "kpiobservationdimension", joinColumns = @JoinColumn(name = "observationId"), inverseJoinColumns = @JoinColumn(name = "dimensionId")) @JoinTable(name = "kpiobservationdimension", joinColumns = @JoinColumn(name = "observationId"), inverseJoinColumns = @JoinColumn(name = "dimensionKeyId"))
private Set<DimensionKey> dimensions = new HashSet<DimensionKey>(); private Set<DimensionKey> dimensions = new HashSet<DimensionKey>();
@Column @Column
......
package org.genesys2.server.service; package org.genesys2.server.service;
import java.util.List; import java.util.List;
import java.util.Set;
import org.genesys2.server.model.kpi.Dimension; import org.genesys2.server.model.kpi.Dimension;
import org.genesys2.server.model.kpi.Execution; import org.genesys2.server.model.kpi.Execution;
...@@ -24,7 +25,7 @@ public interface KPIService { ...@@ -24,7 +25,7 @@ public interface KPIService {
Dimension<?> getDimension(long id); Dimension<?> getDimension(long id);
List<?> getValues(JpaDimension loadedJpa); Set<?> getValues(JpaDimension loadedJpa);
Execution save(Execution execution); Execution save(Execution execution);
...@@ -37,4 +38,10 @@ public interface KPIService { ...@@ -37,4 +38,10 @@ public interface KPIService {
void delete(List<Observation> observations); void delete(List<Observation> observations);
List<KPIParameter> listParameters();
List<Dimension<?>> listDimensions();
List<Execution> listExecutions();
} }
...@@ -9,6 +9,7 @@ import javax.persistence.EntityManager; ...@@ -9,6 +9,7 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContext;
import javax.persistence.Query; import javax.persistence.Query;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.genesys2.server.model.kpi.Dimension; import org.genesys2.server.model.kpi.Dimension;
...@@ -20,10 +21,11 @@ import org.genesys2.server.model.kpi.Observation; ...@@ -20,10 +21,11 @@ import org.genesys2.server.model.kpi.Observation;
import org.genesys2.server.persistence.domain.kpi.DimensionKeyRepository; import org.genesys2.server.persistence.domain.kpi.DimensionKeyRepository;
import org.genesys2.server.persistence.domain.kpi.DimensionRepository; import org.genesys2.server.persistence.domain.kpi.DimensionRepository;
import org.genesys2.server.persistence.domain.kpi.ExecutionRepository; import org.genesys2.server.persistence.domain.kpi.ExecutionRepository;
import org.genesys2.server.persistence.domain.kpi.ObservationRepository;
import org.genesys2.server.persistence.domain.kpi.KPIParameterRepository; import org.genesys2.server.persistence.domain.kpi.KPIParameterRepository;
import org.genesys2.server.persistence.domain.kpi.ObservationRepository;
import org.genesys2.server.service.KPIService; import org.genesys2.server.service.KPIService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
...@@ -66,6 +68,11 @@ public class KPIServiceImpl implements KPIService { ...@@ -66,6 +68,11 @@ public class KPIServiceImpl implements KPIService {
return parameterRepository.findByName(name); return parameterRepository.findByName(name);
} }
@Override
public List<KPIParameter> listParameters() {
return parameterRepository.findAll(new Sort("name"));
}
@Override @Override
@Transactional @Transactional
public void delete(KPIParameter parameter) { public void delete(KPIParameter parameter) {
...@@ -87,6 +94,11 @@ public class KPIServiceImpl implements KPIService { ...@@ -87,6 +94,11 @@ public class KPIServiceImpl implements KPIService {
return dim; return dim;
} }
@Override
public List<Dimension<?>> listDimensions() {
return dimensionRepository.findAll(new Sort("name"));
}
@Override @Override
public Execution getExecution(long id) { public Execution getExecution(long id) {
return executionRepository.findOne(id); return executionRepository.findOne(id);
...@@ -98,6 +110,11 @@ public class KPIServiceImpl implements KPIService { ...@@ -98,6 +110,11 @@ public class KPIServiceImpl implements KPIService {
return executionRepository.save(execution); return executionRepository.save(execution);
} }
@Override
public List<Execution> listExecutions() {
return executionRepository.findAll();
}
@Override @Override
@Transactional @Transactional
public void delete(Execution execution) { public void delete(Execution execution) {
...@@ -105,11 +122,14 @@ public class KPIServiceImpl implements KPIService { ...@@ -105,11 +122,14 @@ public class KPIServiceImpl implements KPIService {
} }
@Override @Override
public List<?> getValues(JpaDimension jpaDimension) { public Set<?> getValues(JpaDimension jpaDimension) {
StringBuilder paQuery = new StringBuilder(); StringBuilder paQuery = new StringBuilder();
paQuery.append("select distinct a.").append(jpaDimension.getField()).append(" from "); paQuery.append("select distinct a.").append(jpaDimension.getField()).append(" from ");
paQuery.append(jpaDimension.getEntity()).append(" a"); paQuery.append(jpaDimension.getEntity()).append(" a");
if (StringUtils.isNotBlank(jpaDimension.getCondition())) {
paQuery.append(" where ").append(jpaDimension.getCondition());
}
LOG.debug(paQuery); LOG.debug(paQuery);
Query q = entityManager.createQuery(paQuery.toString()); Query q = entityManager.createQuery(paQuery.toString());
...@@ -119,7 +139,7 @@ public class KPIServiceImpl implements KPIService { ...@@ -119,7 +139,7 @@ public class KPIServiceImpl implements KPIService {
// q.setParameter(i + 1, params[i]); // q.setParameter(i + 1, params[i]);
// } // }
return q.getResultList(); return new HashSet<Object>(q.getResultList());
} }
@Override @Override
...@@ -179,7 +199,12 @@ public class KPIServiceImpl implements KPIService { ...@@ -179,7 +199,12 @@ public class KPIServiceImpl implements KPIService {
printRes(res, paramExec, params.toArray(), results); printRes(res, paramExec, params.toArray(), results);
} else { } else {
// Recurse // Recurse
for (Object val : dim.getValues()) { Set<?> values = null;
if (dim instanceof JpaDimension) {
values = getValues((JpaDimension) dim);
} else
values = dim.getValues();
for (Object val : values) {
params.add(val); params.add(val);
internalExecute(paQuery, paramExec, results, depth + 1, params); internalExecute(paQuery, paramExec, results, depth + 1, params);
params.remove(depth); params.remove(depth);
......
/**
* Copyright 2014 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.servlet.controller.rest;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.ValidationException;
import net.sf.oval.ConstraintViolation;
import net.sf.oval.Validator;
import org.genesys2.server.exception.AuthorizationException;
import org.genesys2.server.model.kpi.BooleanDimension;
import org.genesys2.server.model.kpi.Dimension;
import org.genesys2.server.model.kpi.Execution;
import org.genesys2.server.model.kpi.ExecutionDimension;
import org.genesys2.server.model.kpi.KPIParameter;
import org.genesys2.server.model.kpi.Observation;
import org.genesys2.server.service.KPIService;
import org.genesys2.server.servlet.controller.rest.model.ExecutionDimensionJson;
import org.genesys2.server.servlet.controller.rest.model.ExecutionJson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = { "/api/v0/kpi", "/json/v0/kpi" })
public class KPIController extends RestController {
@Autowired
private KPIService kpiService;
/**
* List parameters
*
* @return
* @throws AuthorizationException
*/
@RequestMapping(value = "/parameter/list", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
Map<String, String> listParameters() {
LOG.info("Listing KPI parameters");
HashMap<String, String> m = new HashMap<>();
for (KPIParameter kpip : kpiService.listParameters()) {
m.put(kpip.getName(), kpip.getTitle() + "\n" + kpip.getDescription());
}
return m;
}
/**
* Get parameter
*
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/parameter/{name:.+}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
KPIParameter getParameter(@PathVariable("name") String name) {
return kpiService.getParameter(name);
}
/**
* Update parameter
*
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/parameter", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
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);
if (violations.size() > 0) {
// TODO We could do better messages on validation error
throw new ModelValidationException("Validation failed", violations);
}
return kpiService.save(parameter);
}
/**
* List dimensions
*
* @return
* @throws AuthorizationException
*/
@RequestMapping(value = "/dimension/list", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
HashMap<Long, String> listDimensions() {
LOG.info("Listing KPI dimensions");
HashMap<Long, String> m = new HashMap<>();
for (Dimension<?> dim : kpiService.listDimensions()) {
m.put(dim.getId(), dim.getName() + " " + dim.getTitle());
}
return m;
}
/**
* Get parameter
*
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/dimension/{id}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
Dimension<?> getDimension(@PathVariable("id") long id) {
return kpiService.getDimension(id);
}
/**
* Update {@link BooleanDimension}
*
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/dimension", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
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);
if (violations.size() > 0) {
// TODO We could do better messages on validation error
throw new ModelValidationException("Validation failed", violations);
}
return kpiService.save(dimension);
}
/**
* List dimensions
*
* @return
* @throws AuthorizationException
*/
@RequestMapping(value = "/execution/list", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
HashMap<Long, String> listExecution() {
LOG.info("Listing KPI executions");
HashMap<Long, String> m = new HashMap<>();
for (Execution exec : kpiService.listExecutions()) {
m.put(exec.getId(), exec.getTitle());
}
return m;
}
/**
* Get parameter
*
* @return
* @throws ValidationException
*/
@RequestMapping(value = "/execution/{id}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
ExecutionJson getExecution(@PathVariable("id") long id) {
Execution execution = kpiService.getExecution(id);
return ExecutionJson.from(execution);
}
/**
* Update {@link BooleanDimension}
*
* @return
*
* @return
* @throws ValidationException
*/