Skip to content

Commit

Permalink
#184 improve KPIs; support for Metric chart type
Browse files Browse the repository at this point in the history
  • Loading branch information
teosarca committed Feb 25, 2017
1 parent 99de068 commit eabe06a
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,15 @@ public void changeKPIsDashboard(@RequestBody final JSONDashboardChanges jsonDash
@GetMapping("/kpis/{itemId}/data")
public KPIData getKPIData( //
@PathVariable final int itemId //
, @RequestParam("fromMillis") @ApiParam("interval rage start, in case of temporal data") final long fromMillis //
, @RequestParam("toMillis") @ApiParam("interval rage end, in case of temporal data") final long toMillis //
, @RequestParam(name = "fromMillis", required = false, defaultValue = "0") @ApiParam("interval rage start, in case of temporal data") final long fromMillis //
, @RequestParam(name = "toMillis", required = false, defaultValue = "0") @ApiParam("interval rage end, in case of temporal data") final long toMillis //
)
{
return userDashboardRepo.getUserDashboard()
.getKPIItemById(itemId)
.getKPI()
.retrieveData(fromMillis, toMillis);
.retrieveData(fromMillis, toMillis)
.setItemId(itemId);
}

@GetMapping("/targetIndicators")
Expand All @@ -97,13 +98,14 @@ public JSONDashboard getTargetIndicatorsDashboard()
@GetMapping("/targetIndicators/{itemId}/data")
public KPIData getTargetIndicatorData( //
@PathVariable final int itemId //
, @RequestParam("fromMillis") @ApiParam("interval rage start, in case of temporal data") final long fromMillis //
, @RequestParam("toMillis") @ApiParam("interval rage end, in case of temporal data") final long toMillis //
, @RequestParam(name = "fromMillis", required = false, defaultValue = "0") @ApiParam("interval rage start, in case of temporal data") final long fromMillis //
, @RequestParam(name = "toMillis", required = false, defaultValue = "0") @ApiParam("interval rage end, in case of temporal data") final long toMillis //
)
{
return userDashboardRepo.getUserDashboard()
.getTargetIndicatorItemById(itemId)
.getKPI()
.retrieveData(fromMillis, toMillis);
.retrieveData(fromMillis, toMillis)
.setItemId(itemId);
}
}
106 changes: 72 additions & 34 deletions metasfresh-webui-api/src/main/java/de/metas/ui/web/dashboard/KPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket;
import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregation;

import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -70,11 +71,24 @@ public static final Builder builder()
private KPI(final Builder builder)
{
super();

Check.assume(builder.id > 0, "id > 0");
Check.assumeNotNull(builder.caption, "Parameter builder.caption is not null");
Check.assumeNotNull(builder.description, "Parameter builder.description is not null");
Check.assumeNotNull(builder.chartType, "Parameter builder.chartType is not null");
Check.assumeNotEmpty(builder.fields, "builder.fields is not empty");
Check.assumeNotEmpty(builder.esSearchIndex, "builder.esSearchIndex is not empty");
Check.assumeNotEmpty(builder.esSearchTypes, "builder.esSearchTypes is not empty");
Check.assumeNotEmpty(builder.esQuery, "builder.esQuery is not empty");
Check.assumeNotNull(builder.elasticsearchClient, "Parameter builder.elasticsearchClient is not null");

id = builder.id;

caption = builder.caption;
description = builder.description;
chartType = builder.chartType;
timeRange = builder.timeRange;

fields = ImmutableList.copyOf(builder.fields);

esSearchIndex = builder.esSearchIndex;
Expand Down Expand Up @@ -118,11 +132,36 @@ public List<KPIField> getFields()
return fields;
}

public KPIData retrieveData(final long fromMillis, final long toMillis)
public KPIData retrieveData(long fromMillis, long toMillis)
{
//
// Create query evaluation context
if (toMillis <= 0)
{
toMillis = SystemTime.millis();
}

if (fromMillis <= 0)
{
if (timeRange.isZero())
{
fromMillis = 0;
}
else
{
fromMillis = toMillis - timeRange.abs().toMillis();
}
}

final Evaluatee evalCtx = Evaluatees.mapBuilder()
.put("FromMillis", fromMillis)
.put("ToMillis", toMillis)
.build()
// Fallback to user context
.andComposeWith(Evaluatees.ofCtx(Env.getCtx()));

//
// Resolve esQuery's variables
final Evaluatee evalCtx = createEvalCtx(fromMillis, toMillis);
final String esQueryParsed = esQuery.evaluate(evalCtx, OnVariableNotFound.Preserve);

//
Expand All @@ -137,6 +176,7 @@ public KPIData retrieveData(final long fromMillis, final long toMillis)
}
catch (final NoNodeAvailableException e)
{
// elastic search transport error => nothing to do about it
throw e;
}
catch (final Exception e)
Expand All @@ -158,7 +198,8 @@ public KPIData retrieveData(final long fromMillis, final long toMillis)
}
final Aggregation agg = aggs.get(0);

final KPIData.Builder data = KPIData.builder();
final KPIData.Builder data = KPIData.builder()
.setTimeRange(fromMillis, toMillis);
if (agg instanceof MultiBucketsAggregation)
{
final String aggName = agg.getName();
Expand All @@ -171,14 +212,40 @@ public KPIData retrieveData(final long fromMillis, final long toMillis)
final ImmutableList.Builder<Object> values = ImmutableList.builder();
for (final KPIField field : getFields())
{
final Object value = field.getValueExtractor().extractValue(aggName, bucket);
final Object value = field.getBucketValueExtractor().extractValue(aggName, bucket);
final Object jsonValue = field.convertValueToJson(value);
values.add(jsonValue == null ? Optional.empty() : jsonValue);
}

data.addValue(new KPIValue(key, values.build()));
}
}
else if (agg instanceof NumericMetricsAggregation.SingleValue)
{
final NumericMetricsAggregation.SingleValue singleValueAggregation = (NumericMetricsAggregation.SingleValue)agg;

final String key = null; // N/A

final ImmutableList.Builder<Object> values = ImmutableList.builder();
for (final KPIField field : getFields())
{
final Object value;
if ("value".equals(field.getESPathAsString()))
{
value = singleValueAggregation.value();
}
else
{
throw new IllegalStateException("ES_Path not supported for " + field);
}

final Object jsonValue = field.convertValueToJson(value);
values.add(jsonValue == null ? Optional.empty() : jsonValue);
}

data.addValue(new KPIValue(key, values.build()));

}
else
{
throw new IllegalStateException("Aggregation type not supported: " + agg.getClass());
Expand All @@ -195,38 +262,10 @@ public KPIData retrieveData(final long fromMillis, final long toMillis)
}
}

private Evaluatee createEvalCtx(long fromMillis, long toMillis)
{
if (toMillis <= 0)
{
toMillis = SystemTime.millis();
}

if (fromMillis <= 0)
{
if (timeRange.isZero())
{
fromMillis = 0;
}
else
{
fromMillis = toMillis - timeRange.abs().toMillis();
}
}

return Evaluatees.mapBuilder()
.put("FromMillis", fromMillis)
.put("ToMillis", toMillis)
.build()
// Fallback to user context
.andComposeWith(Evaluatees.ofCtx(Env.getCtx()));

}

public static final class Builder
{
private int id;
private ITranslatableString caption;
private ITranslatableString caption = ImmutableTranslatableString.empty();
private ITranslatableString description = ImmutableTranslatableString.empty();
private KPIChartType chartType;
private Duration timeRange = Duration.ZERO;
Expand All @@ -244,7 +283,6 @@ private Builder()

public KPI build()
{
Check.assume(id > 0, "id > 0");
return new KPI(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public enum KPIChartType
AreaChart(X_WEBUI_KPI.CHARTTYPE_AreaChart) //
, BarChart(X_WEBUI_KPI.CHARTTYPE_BarChart) //
, PieChart(X_WEBUI_KPI.CHARTTYPE_PieChart) //
, Metric(X_WEBUI_KPI.CHARTTYPE_Metric) //
;
private String code;

Expand Down Expand Up @@ -63,6 +64,7 @@ public static KPIChartType forCode(final String code)
.put(AreaChart.getCode(), AreaChart)
.put(BarChart.getCode(), BarChart)
.put(PieChart.getCode(), PieChart)
.put(Metric.getCode(), Metric)
.build();

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableList;

/*
Expand Down Expand Up @@ -35,22 +37,44 @@ public static Builder builder()
{
return new Builder();
}

@JsonProperty("itemId")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer itemId;

@JsonProperty("data")
private final List<KPIValue> data;

private KPIData(final List<KPIValue> data)
@JsonProperty("fromMillis")
@JsonInclude(JsonInclude.Include.NON_NULL)
private final Long fromMillis;
@JsonProperty("toMillis")
@JsonInclude(JsonInclude.Include.NON_NULL)
private final Long toMillis;

private KPIData(final Builder builder)
{
this.data = data;
data = builder.data.build();
fromMillis = builder.fromMillis <= 0 ? null : builder.fromMillis;
toMillis = builder.toMillis <= 0 ? null : builder.toMillis;
}

public List<KPIValue> getData()
{
return data;
}

public KPIData setItemId(int itemId)
{
this.itemId = itemId;
return this;
}

public static final class Builder
{
private final ImmutableList.Builder<KPIValue> data = ImmutableList.builder();
private long fromMillis;
private long toMillis;

private Builder()
{
Expand All @@ -59,7 +83,14 @@ private Builder()

public KPIData build()
{
return new KPIData(data.build());
return new KPIData(this);
}

public Builder setTimeRange(final long fromMillis, final long toMillis)
{
this.fromMillis = fromMillis;
this.toMillis = toMillis;
return this;
}

public Builder addValue(final KPIValue value)
Expand Down
Loading

0 comments on commit eabe06a

Please sign in to comment.