Commit 3af55efa authored by Matija Obreza's avatar Matija Obreza

KPI Execution allows for joining collections and summarizing on their properties

parent f644e7d3
......@@ -37,14 +37,32 @@ public class ApiError<T extends Throwable> {
* @param ex the ex
*/
public ApiError(final T ex) {
if (StringUtils.isBlank(ex.getMessage())) {
this.error = ex.getClass().getSimpleName();
} else {
this.error = ex.getMessage();
}
this.error = makeMessage(ex);
this.localizedError = ex.getLocalizedMessage();
}
/**
* Collect exception messages
*
* @param ex
* @return all exception messages
*/
private String makeMessage(Throwable ex) {
StringBuilder sb = new StringBuilder();
while (ex != null) {
if (sb.length() > 0) {
sb.append('\n');
}
if (StringUtils.isBlank(ex.getMessage())) {
sb.append(ex.getClass().getSimpleName());
} else {
sb.append(ex.getMessage());
}
ex = ex.getCause();
}
return sb.toString();
}
/**
* Gets the error.
*
......
......@@ -30,6 +30,7 @@ import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.genesys.blocks.model.AuditedVersionedModel;
......@@ -85,6 +86,12 @@ public class Execution extends AuditedVersionedModel implements SelfCleaning {
@JoinColumn(name = "parameterId")
private KPIParameter parameter;
/** This joins the _base.link in the query and allows us to count properties of collections */
@Size(min = 1, max = 50)
@Pattern(regexp="[a-z]([a-zA-Z0-9_]+)")
@Column(length = 50)
private String link;
/** This is the property of {@link #parameter} we're observing */
@NotNull
@Size(max = 30)
......@@ -133,6 +140,20 @@ public class Execution extends AuditedVersionedModel implements SelfCleaning {
public ExecutionType getType() {
return type;
}
/**
* Set the name of the collection to join to the query. The {@link #property} then referes
* to the joined collection.
*
* @param link Collection property of the entity to join
*/
public void setLink(String link) {
this.link = link;
}
public String getLink() {
return link;
}
/**
* Set the property of the {{@link #parameter} that we're observing. Defaults to
......@@ -185,14 +206,14 @@ public class Execution extends AuditedVersionedModel implements SelfCleaning {
}
sb.append(" from ");
sb.append(parameter.getEntity());
sb.append(" ").append(alias);
sb.append(" ").append(link == null ? alias : "X");
int pedC = 0;
for (ExecutionDimension ped : dimensions) {
pedC++;
if (ped.getLink() != null) {
sb.append(" inner join ");
sb.append(alias).append(".");
sb.append(link == null ? alias : "X").append(".");
sb.append(ped.getLink());
sb.append(" _ped").append(pedC).append(" ");
}
......@@ -201,16 +222,24 @@ public class Execution extends AuditedVersionedModel implements SelfCleaning {
where.append(" and ");
if (ped.getLink() == null) {
where.append("( ").append(alias).append(".").append(ped.getField()).append(" = ?").append(pedC).append(" )");
where.append("( ").append(link == null ? alias : "X").append(".").append(ped.getField()).append(" = ?").append(pedC).append(" )");
} else {
where.append("( _ped").append(pedC).append(".").append(ped.getField()).append(" = ?").append(pedC).append(" )");
}
}
if (link != null) {
// We're joining a collection to count it's property
sb.append(" inner join ");
sb.append("X.");
sb.append(link);
sb.append(" ").append(alias);
}
if (where.length() > 0 || parameter.getCondition() != null) {
sb.append(" where ");
if (parameter.getCondition() != null) {
sb.append(alias).append(".").append(parameter.getCondition());
sb.append(link == null ? alias : "X").append(".").append(parameter.getCondition());
}
if (dimensions.size() > 0) {
if (parameter.getCondition() != null) {
......
......@@ -66,8 +66,8 @@ public class KPIParameter extends AuditedVersionedModel implements SelfCleaning
private String title;
@NotNull
@Pattern(regexp = "[a-z][a-zA-Z0-9_]*")
@Size(max = 100)
@Pattern(regexp = "[A-Z][a-zA-Z0-9_]*")
@Size(min = 1, max = 100)
@Column(length = 100, nullable = false)
private String entity;
......
......@@ -270,7 +270,7 @@ public class KPIServiceImpl implements KPIService {
List<Observation> results = new ArrayList<Observation>();
execution = executionRepository.findOne(execution.getId());
LOG.debug(execution.query());
LOG.info(execution.query());
Query query = entityManager.createQuery(execution.query());
internalExecute(query, execution, results, 0, new ArrayList<Object>());
......@@ -515,6 +515,7 @@ public class KPIServiceImpl implements KPIService {
target.getExecutionDimensions().clear();
target.getExecutionDimensions().addAll(source.getExecutionDimensions().stream().filter(executionDimension -> !target.getExecutionDimensions().contains(executionDimension)).collect(Collectors.toList()));
}
target.setLink(source.getLink());
target.setProperty(source.getProperty());
target.setParameter(source.getParameter());
target.setTitle(source.getTitle());
......
......@@ -4518,21 +4518,35 @@ databaseChangeLog:
- dropTable:
tableName: partner_wiews
# ENABLE AFTER SOME TIME
# - changeSet:
# id: 1537463144763-folder
# author: mobreza
# comment: Require folder_id
# changes:
# - addNotNullConstraint:
# columnDataType: BIGINT
# columnName: folder_id
# tableName: repository_document
# - addNotNullConstraint:
# columnDataType: BIGINT
# columnName: folder_id
# tableName: repository_image
# - addNotNullConstraint:
# columnDataType: BIGINT
# columnName: folder_id
# tableName: repository)file
- changeSet:
id: 1537463144763-folder
author: mobreza
comment: Require folder_id
changes:
- addNotNullConstraint:
columnDataType: BIGINT
columnName: folder_id
tableName: repository_document
- addNotNullConstraint:
columnDataType: BIGINT
columnName: folder_id
tableName: repository_image
- addNotNullConstraint:
columnDataType: BIGINT
columnName: folder_id
tableName: repository_file
- changeSet:
id: 1545064422000-1
author: matijaobreza
comment: Add `link` property to KPI Execution for joins
changes:
- addColumn:
tableName: kpiexecution
columns:
- column:
constraints:
nullable: true
name: link
type: varchar(50)
......@@ -18,12 +18,14 @@ package org.genesys.test.server.services;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomUtils;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.server.model.impl.Organization;
import org.genesys2.server.model.kpi.BooleanDimension;
import org.genesys2.server.model.kpi.Execution;
import org.genesys2.server.model.kpi.Execution.ExecutionType;
......@@ -34,18 +36,21 @@ import org.genesys2.server.model.kpi.NumericListDimension;
import org.genesys2.server.model.kpi.Observation;
import org.genesys2.server.model.kpi.StringListDimension;
import org.genesys2.server.persistence.FaoInstituteRepository;
import org.genesys2.server.persistence.OrganizationRepository;
import org.genesys2.server.persistence.kpi.DimensionRepository;
import org.genesys2.server.persistence.kpi.ExecutionRepository;
import org.genesys2.server.persistence.kpi.ExecutionRunRepository;
import org.genesys2.server.persistence.kpi.KPIParameterRepository;
import org.genesys2.server.persistence.kpi.ObservationRepository;
import org.genesys2.server.service.KPIService;
import org.genesys2.server.service.OrganizationService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.test.context.support.WithMockUser;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
......@@ -70,6 +75,10 @@ public class KPIServiceTest extends AbstractServicesTest {
@Autowired
private FaoInstituteRepository instituteRepository;
@Autowired
private OrganizationRepository organizationRepository;
@Autowired
private OrganizationService organizationService;
@Override
......@@ -77,6 +86,7 @@ public class KPIServiceTest extends AbstractServicesTest {
super.beforeTest();
assertThat(paramRepository.count(), is(0l));
List<FaoInstitute> institutes = new ArrayList<>();
for (int i=0; i<10; i++) {
FaoInstitute institute=new FaoInstitute();
institute.setAccessionCount(100+i);
......@@ -86,9 +96,23 @@ public class KPIServiceTest extends AbstractServicesTest {
institute.setElevation(RandomUtils.nextDouble(0, 2000));
// Replaced by code
institute.setvCode(i > 1 && RandomUtils.nextBoolean() ? null : "INS" + (i - 2));
instituteRepository.save(institute);
institutes.add(instituteRepository.save(institute));
}
assertThat(instituteRepository.count(), is(10l));
{
Organization org = new Organization();
org.setSlug("ORG1");
org = organizationRepository.save(org);
organizationService.addOrganizationInstitutes(org, institutes.stream().map(inst -> inst.getCode()).collect(Collectors.toList()));
}
{
Organization org = new Organization();
org.setSlug("ORG2");
org = organizationRepository.save(org);
organizationService.addOrganizationInstitutes(org, institutes.stream().filter(inst -> inst.isAllowMaterialRequests()).map(inst -> inst.getCode()).collect(Collectors.toList()));
}
assertThat(organizationRepository.count(), is(2l));
}
@Override
......@@ -98,6 +122,11 @@ public class KPIServiceTest extends AbstractServicesTest {
paramRepository.deleteAll();
dimRepository.deleteAll();
for (Organization organization: organizationRepository.findAll()) {
organizationService.setOrganizationInstitutes(organization, Lists.newArrayList());
}
organizationRepository.deleteAll();
instituteRepository.deleteAll();
super.cleanup();
}
......@@ -363,6 +392,75 @@ public class KPIServiceTest extends AbstractServicesTest {
assertThat(obsRepository.count(), is(0l));
}
@Test
public void testExecutionLinkedCollection() {
KPIParameter param1 = kpiService.save(createParameter("institute", FaoInstitute.class, "Institute"));
Execution exec1 = new Execution();
exec1.setName("institute.count");
exec1.setTitle("Institute count");
exec1.setParameter(param1);
exec1.setLink("networks"); // @OneToMany in FaoInstitute
exec1.setProperty("slug"); // a property in Organization
Execution loaded = kpiService.save(exec1);
assertThat(loaded, notNullValue());
assertThat(loaded.getId(), notNullValue());
assertThat(loaded.getName(), is("institute.count"));
assertThat(loaded.getTitle(), is("Institute count"));
// defaults
assertThat(loaded.getProperty(), is("slug"));
assertThat(loaded.getLink(), is("networks"));
assertThat(loaded.getType(), is(Execution.ExecutionType.COUNT));
List<Observation> results = kpiService.execute(loaded);
assertThat(results, notNullValue());
// results.forEach(r -> {
// System.err.println(r);
// });
assertThat(results, hasSize(1));
assertThat(results.stream().collect(Collectors.summingDouble(Observation::getValue)), is(2d)); // distinct slugs of organizations of all institutes
kpiService.delete(loaded);
assertThat(execRepository.count(), is(0l));
}
@Test
public void testExecutionLinkedCollectionCountByBoolean1() {
KPIParameter param1 = kpiService.save(createParameter("institute", FaoInstitute.class, "Institute"));
Execution exec1 = new Execution();
exec1.setName("institute.count");
exec1.setTitle("Institute count");
exec1.setParameter(param1);
exec1.setLink("networks"); // @OneToMany in FaoInstitute
exec1.setProperty("slug"); // a property in Organization
BooleanDimension dim1 = kpiService.save(createBoolDimension("yesno", "Boolean yes and no dimension"));
exec1.addDimension(dim1, null, "allowMaterialRequests");
Execution loaded = kpiService.save(exec1);
assertThat(loaded, notNullValue());
assertThat(loaded.getId(), notNullValue());
assertThat(loaded.getName(), is("institute.count"));
assertThat(loaded.getTitle(), is("Institute count"));
// defaults
assertThat(loaded.getProperty(), is("slug"));
assertThat(loaded.getLink(), is("networks"));
assertThat(loaded.getType(), is(Execution.ExecutionType.COUNT));
List<Observation> results = kpiService.execute(loaded);
assertThat(results, notNullValue());
// results.forEach(r -> {
// System.err.println(r);
// });
assertThat(results, hasSize(2));
assertThat(results.stream().collect(Collectors.summingDouble(Observation::getValue)), is(3d)); // 1+2
for (int i = 0; i < 2; i++) {
assertThat(results.get(i).getDimensions().get(0).getName(), is("yesno"));
assertThat(results.get(i).getDimensions().get(0).getValue(), isOneOf("true", "false"));
assertThat(results.get(i).getValue(), is(results.get(i).getDimensions().get(0).getValue().equals("true") ? 2d : 1d)); // allows=true has 2 org slugs, false has 1
}
kpiService.delete(loaded);
assertThat(execRepository.count(), is(0l));
}
private BooleanDimension createBoolDimension(String name, String title) {
BooleanDimension dim = new BooleanDimension();
......
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