add links to current settings and to current image
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package org.lucares.pdbui;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason = "Internal Server Error")
|
||||
public class InternalServerError extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 548651821080252932L;
|
||||
|
||||
public InternalServerError(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InternalServerError(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.lucares.pdbui;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -12,7 +14,11 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.lucares.pdb.datastore.Proposal;
|
||||
import org.lucares.pdb.plot.api.AxisScale;
|
||||
import org.lucares.pdb.plot.api.Limit;
|
||||
import org.lucares.pdb.plot.api.PlotSettings;
|
||||
import org.lucares.pdb.plot.api.PlotType;
|
||||
import org.lucares.pdbui.domain.Aggregate;
|
||||
import org.lucares.pdbui.domain.AutocompleteProposal;
|
||||
import org.lucares.pdbui.domain.AutocompleteProposalByValue;
|
||||
import org.lucares.pdbui.domain.AutocompleteResponse;
|
||||
@@ -31,6 +37,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -39,6 +46,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
@Controller
|
||||
@EnableAutoConfiguration
|
||||
@@ -105,6 +113,63 @@ public class PdbController implements HardcodedValues {
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/plots", //
|
||||
method = RequestMethod.GET, //
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE //
|
||||
)
|
||||
StreamingResponseBody createPlotImage(@RequestParam(name = "query", defaultValue = "") final String query,
|
||||
@RequestParam(name = "groupBy[]", defaultValue = "") final List<String> aGroupBy,
|
||||
@RequestParam(name = "limitBy.number", defaultValue = "10") final int limit,
|
||||
@RequestParam(name = "limitBy.selected", defaultValue = "NO_LIMIT") final Limit limitBy,
|
||||
@RequestParam(name = "dateFrom", defaultValue = "") final String dateFrom,
|
||||
@RequestParam(name = "dateRange", defaultValue = "1 week") final String dateRange,
|
||||
@RequestParam(name = "axisScale", defaultValue = "LINEAR") final AxisScale axisScale,
|
||||
@RequestParam(name = "plotType", defaultValue = "SCATTER") final PlotType plotType,
|
||||
@RequestParam(name = "aggregate", defaultValue = "NONE") final Aggregate aggregate,
|
||||
@RequestParam(name = "keyOutside", defaultValue = "false") final boolean keyOutside,
|
||||
@RequestParam(name = "height", defaultValue = "1080") final int height,
|
||||
@RequestParam(name = "width", defaultValue = "1920") final int hidth) {
|
||||
return (final OutputStream outputStream) -> {
|
||||
|
||||
if (StringUtils.isBlank(query)) {
|
||||
throw new BadRequest("The query must not be empty!");
|
||||
}
|
||||
|
||||
final PlotSettings plotSettings = new PlotSettings();
|
||||
plotSettings.setQuery(query);
|
||||
plotSettings.setGroupBy(aGroupBy);
|
||||
plotSettings.setHeight(height);
|
||||
plotSettings.setWidth(hidth);
|
||||
plotSettings.setLimit(limit);
|
||||
plotSettings.setLimitBy(limitBy);
|
||||
plotSettings.setDateFrom(dateFrom);
|
||||
plotSettings.setDateRange(dateRange);
|
||||
plotSettings.setYAxisScale(axisScale);
|
||||
plotSettings.setPlotType(plotType);
|
||||
plotSettings.setAggregate(PlotSettingsTransformer.toAggregateInternal(aggregate));
|
||||
plotSettings.setKeyOutside(keyOutside);
|
||||
|
||||
if (plotterLock.tryLock()) {
|
||||
try {
|
||||
final PlotResult result = plotter.plot(plotSettings);
|
||||
|
||||
try (FileInputStream in = new FileInputStream(result.getImagePath().toFile())) {
|
||||
StreamUtils.copy(in, outputStream);
|
||||
}
|
||||
} catch (final NoDataPointsException e) {
|
||||
throw new NotFoundException(e);
|
||||
} catch (final InternalPlottingException e) {
|
||||
throw new InternalServerError(e);
|
||||
} finally {
|
||||
plotterLock.unlock();
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new ServiceUnavailableException("Too many parallel requests!");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/autocomplete", //
|
||||
method = RequestMethod.GET, //
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE //
|
||||
|
||||
@@ -23,15 +23,17 @@ class PlotSettingsTransformer {
|
||||
result.setYAxisScale(request.getAxisScale());
|
||||
result.setPlotType(request.getPlotType());
|
||||
result.setAggregate(toAggregateInternal(request.getAggregate()));
|
||||
result.setKeyOutside(request.isKeyOutside());
|
||||
result.setKeyOutside(request.isKeyOutside());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static AggregateHandler toAggregateInternal(Aggregate aggregate) {
|
||||
static AggregateHandler toAggregateInternal(final Aggregate aggregate) {
|
||||
switch (aggregate) {
|
||||
case NONE:return new NullAggregate();
|
||||
case PERCENTILES:return new PercentileAggregate();
|
||||
case NONE:
|
||||
return new NullAggregate();
|
||||
case PERCENTILES:
|
||||
return new PercentileAggregate();
|
||||
}
|
||||
throw new IllegalStateException("unhandled enum: " + aggregate);
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ Vue.component('result-view', {
|
||||
},
|
||||
computed: {
|
||||
showPrevNext: function() {
|
||||
return data.searchBar.splitBy.values.length > 0 && data.resultView.imageUrl;
|
||||
return data.searchBar.splitBy.values.length > 0 && !data.resultView.loadingGameActive;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
@@ -445,13 +445,12 @@ Vue.component('search-bar', {
|
||||
},
|
||||
computed: {
|
||||
permalink: function() {
|
||||
|
||||
var groupBy = [];
|
||||
data.searchBar.groupByKeys.forEach(function(e){ if (e.selected) {groupBy.push(e.selected);}});
|
||||
|
||||
var params = {
|
||||
'query': data.searchBar.query,
|
||||
'groupBy': groupBy,
|
||||
var groupBy = [];
|
||||
data.searchBar.groupByKeys.forEach(function(e){ if (e.selected) {groupBy.push(e.selected);}});
|
||||
|
||||
var params = {
|
||||
'query': data.searchBar.query,
|
||||
'groupBy': groupBy,
|
||||
'splitByKeys.selected': data.searchBar.splitByKeys.selected,
|
||||
'limitBy.selected': data.searchBar.limitBy.selected,
|
||||
'limitBy.number': data.searchBar.limitBy.number,
|
||||
@@ -459,12 +458,12 @@ Vue.component('search-bar', {
|
||||
'dateRange': data.searchBar.dateRange,
|
||||
'axisScale': data.searchBar.axisScale,
|
||||
'plotType': data.searchBar.plotType,
|
||||
'showAggregate': data.searchBar.showAggregate,
|
||||
'aggregate': data.searchBar.aggregate,
|
||||
'keyOutside': data.searchBar.keyOutside,
|
||||
};
|
||||
|
||||
var link = window.location.origin+ window.location.pathname + "?" + jQuery.param( params );
|
||||
return link;
|
||||
};
|
||||
|
||||
var link = window.location.origin+ window.location.pathname + "?" + jQuery.param( params );
|
||||
return link;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
@@ -554,7 +553,7 @@ Vue.component('search-bar', {
|
||||
|
||||
<div class="group" id="group-show-aggregate" v-show="searchBar.plotType == 'SCATTER'">
|
||||
<label for="show-aggregate">Aggregate:</label>
|
||||
<select id="show-aggregate" v-model="searchBar.showAggregate">
|
||||
<select id="show-aggregate" v-model="searchBar.aggregate">
|
||||
<option value="NONE">-</option>
|
||||
<option value="PERCENTILES">percentiles</option>
|
||||
</select>
|
||||
@@ -578,7 +577,9 @@ Vue.component('search-bar', {
|
||||
v-on:click.prevent.stop="dashboard"
|
||||
><i class="fa fa-object-group" aria-hidden="true"></i> Dashboard</button>
|
||||
-->
|
||||
<a v-bind:href="permalink" title="Permanent Link" class="permalink"><i class="fa fa-link" aria-hidden="true"></i></a>
|
||||
<a v-bind:href="permalink" v-show="permalink" title="Permanent link to the current settings." class="permalink"><i class="fa fa-sliders" aria-hidden="true"></i></a>
|
||||
<a v-bind:href="searchBar.imagelink" v-show="searchBar.imagelink" title="Image Link" class="permalink"><i class="fa fa-image" aria-hidden="true"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>`
|
||||
@@ -638,7 +639,7 @@ var data = {
|
||||
dateRange: GetURLParameter('dateRange','1 week'),
|
||||
axisScale: GetURLParameter('axisScale','LOG10'),
|
||||
plotType: GetURLParameter('plotType','SCATTER'),
|
||||
showAggregate: GetURLParameter('showAggregate','NONE'),
|
||||
aggregate: GetURLParameter('aggregate','NONE'),
|
||||
keyOutside: GetURLParameterBoolean('keyOutside', 'false'),
|
||||
|
||||
splitBy: {
|
||||
@@ -646,12 +647,14 @@ var data = {
|
||||
query: '',
|
||||
values: [],
|
||||
index: 0
|
||||
}
|
||||
},
|
||||
imagelink: ""
|
||||
},
|
||||
resultView: {
|
||||
imageUrl: '',
|
||||
errorMessage: ''
|
||||
}
|
||||
errorMessage: '',
|
||||
loadingGameActive: false
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -659,10 +662,17 @@ function showLoadingIcon()
|
||||
{
|
||||
data.resultView.imageUrl = '';
|
||||
data.resultView.errorMessage = '';
|
||||
data.resultView.loadingGameActive = true;
|
||||
|
||||
startInvaders();
|
||||
}
|
||||
|
||||
function hideLoadingIcon()
|
||||
{
|
||||
data.resultView.loadingGameActive = false;
|
||||
pauseInvaders();
|
||||
}
|
||||
|
||||
function plotCurrent()
|
||||
{
|
||||
showLoadingIcon();
|
||||
@@ -712,14 +722,15 @@ function sendPlotRequest(query){
|
||||
request['dateRange'] = data.searchBar.dateRange;
|
||||
request['axisScale'] = data.searchBar.axisScale;
|
||||
request['plotType'] = data.searchBar.plotType;
|
||||
request['aggregate'] = data.searchBar.showAggregate;
|
||||
request['aggregate'] = data.searchBar.aggregate;
|
||||
request['keyOutside'] = data.searchBar.keyOutside;
|
||||
|
||||
|
||||
var success = function(response){
|
||||
data.resultView.imageUrl = response.imageUrl;
|
||||
data.resultView.errorMessage = '';
|
||||
pauseInvaders();
|
||||
hideLoadingIcon();
|
||||
updateImageLink(query);
|
||||
};
|
||||
var error = function(e) {
|
||||
data.resultView.imageUrl = '';
|
||||
@@ -732,12 +743,36 @@ function sendPlotRequest(query){
|
||||
else{
|
||||
data.resultView.errorMessage = "FAILED: " + JSON.parse(e.responseText).message;
|
||||
}
|
||||
pauseInvaders();
|
||||
hideLoadingIcon();
|
||||
};
|
||||
|
||||
data.searchBar.imagelink = '';
|
||||
postJson("plots", request, success, error);
|
||||
}
|
||||
|
||||
function updateImageLink(query) {
|
||||
|
||||
var groupBy = [];
|
||||
data.searchBar.groupByKeys.forEach(function(e){ if (e.selected) {groupBy.push(e.selected);}});
|
||||
|
||||
var params = {
|
||||
'query': query,
|
||||
'groupBy': groupBy,
|
||||
'splitByKeys.selected': data.searchBar.splitByKeys.selected,
|
||||
'limitBy.selected': data.searchBar.limitBy.selected,
|
||||
'limitBy.number': data.searchBar.limitBy.number,
|
||||
'dateFrom': data.searchBar.dateFrom,
|
||||
'dateRange': data.searchBar.dateRange,
|
||||
'axisScale': data.searchBar.axisScale,
|
||||
'plotType': data.searchBar.plotType,
|
||||
'aggregate': data.searchBar.aggregate,
|
||||
'keyOutside': data.searchBar.keyOutside,
|
||||
'width': $('#result-image').width(),
|
||||
'height': $('#result-image').height()
|
||||
};
|
||||
|
||||
data.searchBar.imagelink = window.location.origin+ window.location.pathname + "plots?" + jQuery.param(params);
|
||||
}
|
||||
|
||||
function postJson(url, requestData, successCallback, errorCallback) {
|
||||
|
||||
@@ -748,8 +783,7 @@ function postJson(url, requestData, successCallback, errorCallback) {
|
||||
contentType: 'application/json'
|
||||
})
|
||||
.done(successCallback)
|
||||
.fail(errorCallback)
|
||||
;
|
||||
.fail(errorCallback);
|
||||
}
|
||||
|
||||
function getJson(url, requestData, successCallback, errorCallback) {
|
||||
|
||||
Reference in New Issue
Block a user