make it possible to draw the legend outside of the plot area
This commit is contained in:
@@ -1,171 +1,181 @@
|
|||||||
package org.lucares.pdb.plot.api;
|
package org.lucares.pdb.plot.api;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class PlotSettings {
|
public class PlotSettings {
|
||||||
|
|
||||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
private String query;
|
private String query;
|
||||||
|
|
||||||
private int height;
|
private int height;
|
||||||
|
|
||||||
private int width;
|
private int width;
|
||||||
|
|
||||||
private List<String> groupBy;
|
private List<String> groupBy;
|
||||||
|
|
||||||
private Limit limitBy;
|
private Limit limitBy;
|
||||||
|
|
||||||
private int limit;
|
private int limit;
|
||||||
|
|
||||||
private String dateFrom;
|
private String dateFrom;
|
||||||
|
|
||||||
private String dateRange;
|
private String dateRange;
|
||||||
|
|
||||||
private AxisScale yAxisScale;
|
private AxisScale yAxisScale;
|
||||||
|
|
||||||
private AggreateInternal aggregate;
|
private AggreateInternal aggregate;
|
||||||
|
|
||||||
public String getQuery() {
|
private boolean keyOutside;
|
||||||
return query;
|
|
||||||
}
|
public String getQuery() {
|
||||||
|
return query;
|
||||||
public void setQuery(final String query) {
|
}
|
||||||
this.query = query;
|
|
||||||
}
|
public void setQuery(final String query) {
|
||||||
|
this.query = query;
|
||||||
public int getHeight() {
|
}
|
||||||
return height;
|
|
||||||
}
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
public void setHeight(final int height) {
|
}
|
||||||
this.height = height;
|
|
||||||
}
|
public void setHeight(final int height) {
|
||||||
|
this.height = height;
|
||||||
public int getWidth() {
|
}
|
||||||
return width;
|
|
||||||
}
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
public void setWidth(final int width) {
|
}
|
||||||
this.width = width;
|
|
||||||
}
|
public void setWidth(final int width) {
|
||||||
|
this.width = width;
|
||||||
public List<String> getGroupBy() {
|
}
|
||||||
return groupBy;
|
|
||||||
}
|
public List<String> getGroupBy() {
|
||||||
|
return groupBy;
|
||||||
public void setGroupBy(final List<String> groupBy) {
|
}
|
||||||
this.groupBy = groupBy;
|
|
||||||
}
|
public void setGroupBy(final List<String> groupBy) {
|
||||||
|
this.groupBy = groupBy;
|
||||||
public Limit getLimitBy() {
|
}
|
||||||
return limitBy;
|
|
||||||
}
|
public Limit getLimitBy() {
|
||||||
|
return limitBy;
|
||||||
public void setLimitBy(final Limit limitBy) {
|
}
|
||||||
this.limitBy = limitBy;
|
|
||||||
}
|
public void setLimitBy(final Limit limitBy) {
|
||||||
|
this.limitBy = limitBy;
|
||||||
public int getLimit() {
|
}
|
||||||
return limit;
|
|
||||||
}
|
public int getLimit() {
|
||||||
|
return limit;
|
||||||
public void setLimit(final int limit) {
|
}
|
||||||
this.limit = limit;
|
|
||||||
}
|
public void setLimit(final int limit) {
|
||||||
|
this.limit = limit;
|
||||||
public String getDateFrom() {
|
}
|
||||||
return dateFrom;
|
|
||||||
}
|
public String getDateFrom() {
|
||||||
|
return dateFrom;
|
||||||
public void setDateFrom(final String dateFrom) {
|
}
|
||||||
this.dateFrom = dateFrom;
|
|
||||||
}
|
public void setDateFrom(final String dateFrom) {
|
||||||
|
this.dateFrom = dateFrom;
|
||||||
public String getDateRange() {
|
}
|
||||||
return dateRange;
|
|
||||||
}
|
public String getDateRange() {
|
||||||
|
return dateRange;
|
||||||
public void setDateRange(final String dateRange) {
|
}
|
||||||
this.dateRange = dateRange;
|
|
||||||
}
|
public void setDateRange(final String dateRange) {
|
||||||
|
this.dateRange = dateRange;
|
||||||
public OffsetDateTime dateFrom() {
|
}
|
||||||
|
|
||||||
if (StringUtils.isEmpty(dateFrom)) {
|
public OffsetDateTime dateFrom() {
|
||||||
|
|
||||||
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(Long.MIN_VALUE), ZoneOffset.UTC);
|
if (StringUtils.isEmpty(dateFrom)) {
|
||||||
} else {
|
|
||||||
return LocalDateTime.parse(dateFrom, DATE_FORMAT).atOffset(ZoneOffset.UTC);
|
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(Long.MIN_VALUE), ZoneOffset.UTC);
|
||||||
}
|
} else {
|
||||||
}
|
return LocalDateTime.parse(dateFrom, DATE_FORMAT).atOffset(ZoneOffset.UTC);
|
||||||
|
}
|
||||||
public OffsetDateTime dateTo() {
|
}
|
||||||
|
|
||||||
if (StringUtils.isEmpty(dateRange)) {
|
public OffsetDateTime dateTo() {
|
||||||
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(Long.MAX_VALUE), ZoneOffset.UTC);
|
|
||||||
} else {
|
if (StringUtils.isEmpty(dateRange)) {
|
||||||
final int period = Integer.parseInt(dateRange.split(" ")[0]);
|
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(Long.MAX_VALUE), ZoneOffset.UTC);
|
||||||
final ChronoUnit unit = toChronoUnit(dateRange.split(" ")[1]);
|
} else {
|
||||||
|
final int period = Integer.parseInt(dateRange.split(" ")[0]);
|
||||||
return dateFrom().plus(period, unit);
|
final ChronoUnit unit = toChronoUnit(dateRange.split(" ")[1]);
|
||||||
}
|
|
||||||
}
|
return dateFrom().plus(period, unit);
|
||||||
|
}
|
||||||
private ChronoUnit toChronoUnit(final String string) {
|
}
|
||||||
|
|
||||||
switch (string) {
|
private ChronoUnit toChronoUnit(final String string) {
|
||||||
case "second":
|
|
||||||
case "seconds":
|
switch (string) {
|
||||||
return ChronoUnit.SECONDS;
|
case "second":
|
||||||
case "minute":
|
case "seconds":
|
||||||
case "minutes":
|
return ChronoUnit.SECONDS;
|
||||||
return ChronoUnit.MINUTES;
|
case "minute":
|
||||||
case "hour":
|
case "minutes":
|
||||||
case "hours":
|
return ChronoUnit.MINUTES;
|
||||||
return ChronoUnit.HOURS;
|
case "hour":
|
||||||
case "day":
|
case "hours":
|
||||||
case "days":
|
return ChronoUnit.HOURS;
|
||||||
return ChronoUnit.DAYS;
|
case "day":
|
||||||
case "week":
|
case "days":
|
||||||
case "weeks":
|
return ChronoUnit.DAYS;
|
||||||
return ChronoUnit.WEEKS;
|
case "week":
|
||||||
case "month":
|
case "weeks":
|
||||||
case "months":
|
return ChronoUnit.WEEKS;
|
||||||
return ChronoUnit.MONTHS;
|
case "month":
|
||||||
default:
|
case "months":
|
||||||
throw new IllegalArgumentException(string + " is an unknown chrono unit");
|
return ChronoUnit.MONTHS;
|
||||||
}
|
default:
|
||||||
}
|
throw new IllegalArgumentException(string + " is an unknown chrono unit");
|
||||||
|
}
|
||||||
public void setYAxisScale(final AxisScale axisScale) {
|
}
|
||||||
this.yAxisScale = axisScale;
|
|
||||||
}
|
public void setYAxisScale(final AxisScale axisScale) {
|
||||||
|
this.yAxisScale = axisScale;
|
||||||
public AxisScale getYAxisScale() {
|
}
|
||||||
return yAxisScale;
|
|
||||||
}
|
public AxisScale getYAxisScale() {
|
||||||
|
return yAxisScale;
|
||||||
@Override
|
}
|
||||||
public String toString() {
|
|
||||||
return "PlotSettings [query=" + query + ", height=" + height + ", width=" + width + ", groupBy=" + groupBy
|
@Override
|
||||||
+ ", limitBy=" + limitBy + ", limit=" + limit + ", dateFrom=" + dateFrom + ", dateRange=" + dateRange
|
public String toString() {
|
||||||
+ ", axisScale=" + yAxisScale + ", aggregate="+aggregate+"]";
|
return "PlotSettings [query=" + query + ", height=" + height + ", width=" + width + ", groupBy=" + groupBy
|
||||||
}
|
+ ", limitBy=" + limitBy + ", limit=" + limit + ", dateFrom=" + dateFrom + ", dateRange=" + dateRange
|
||||||
|
+ ", axisScale=" + yAxisScale + ", aggregate="+aggregate+", keyOutside="+keyOutside+"]";
|
||||||
public void setAggregate(AggreateInternal aggregate) {
|
}
|
||||||
this.aggregate = aggregate;
|
|
||||||
}
|
public void setAggregate(AggreateInternal aggregate) {
|
||||||
|
this.aggregate = aggregate;
|
||||||
public AggreateInternal getAggregate() {
|
}
|
||||||
return aggregate;
|
|
||||||
}
|
public AggreateInternal getAggregate() {
|
||||||
}
|
return aggregate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyOutside(boolean keyOutside) {
|
||||||
|
this.keyOutside = keyOutside;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isKeyOutside() {
|
||||||
|
return keyOutside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,87 +1,92 @@
|
|||||||
package org.lucares.recommind.logs;
|
package org.lucares.recommind.logs;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.lucares.pdb.plot.api.AggreateInternal;
|
import org.lucares.pdb.plot.api.AggreateInternal;
|
||||||
import org.lucares.pdb.plot.api.AxisScale;
|
import org.lucares.pdb.plot.api.AxisScale;
|
||||||
|
|
||||||
public class GnuplotFileGenerator {
|
public class GnuplotFileGenerator {
|
||||||
|
|
||||||
public String generate(final GnuplotSettings settings, final Collection<DataSeries> dataSeries) {
|
public String generate(final GnuplotSettings settings, final Collection<DataSeries> dataSeries) {
|
||||||
|
|
||||||
final StringBuilder result = new StringBuilder();
|
final StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
appendfln(result, "set terminal %s noenhanced size %d,%d", settings.getTerminal(), settings.getWidth(),
|
appendfln(result, "set terminal %s noenhanced size %d,%d", settings.getTerminal(), settings.getWidth(),
|
||||||
settings.getHeight());
|
settings.getHeight());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
appendfln(result, "set datafile separator \"%s\"", settings.getDatafileSeparator());
|
appendfln(result, "set datafile separator \"%s\"", settings.getDatafileSeparator());
|
||||||
|
|
||||||
int count = 1;
|
int count = 1;
|
||||||
if (settings.getAggregate() != AggreateInternal.NONE)
|
if (settings.getAggregate() != AggreateInternal.NONE)
|
||||||
{
|
{
|
||||||
for (final DataSeries dataSerie : dataSeries) {
|
for (final DataSeries dataSerie : dataSeries) {
|
||||||
|
|
||||||
appendfln(result, "stats '%s' using 2 prefix \"A%d\"", dataSerie.getDataFile(),count);
|
appendfln(result, "stats '%s' using 2 prefix \"A%d\"", dataSerie.getDataFile(),count);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appendfln(result, "set timefmt '%s'", settings.getTimefmt());
|
appendfln(result, "set timefmt '%s'", settings.getTimefmt());
|
||||||
|
|
||||||
appendfln(result, "set xdata time");
|
appendfln(result, "set xdata time");
|
||||||
appendfln(result, "set format x \"%s\"", settings.getFormatX());
|
appendfln(result, "set format x \"%s\"", settings.getFormatX());
|
||||||
appendfln(result, "set xlabel \"%s\"", settings.getXlabel());
|
appendfln(result, "set xlabel \"%s\"", settings.getXlabel());
|
||||||
appendfln(result, "set xtics rotate by %d", settings.getRotateXAxisLabel());
|
appendfln(result, "set xtics rotate by %d", settings.getRotateXAxisLabel());
|
||||||
appendfln(result, "set xrange [\"%s\":\"%s\"]", settings.getDateFrom(), settings.getDateTo());
|
appendfln(result, "set xrange [\"%s\":\"%s\"]", settings.getDateFrom(), settings.getDateTo());
|
||||||
|
|
||||||
final long graphOffset = settings.getYAxisScale() == AxisScale.LINEAR ? 0 : 1;
|
final long graphOffset = settings.getYAxisScale() == AxisScale.LINEAR ? 0 : 1;
|
||||||
appendfln(result, "set yrange [\""+graphOffset+"\":]");
|
appendfln(result, "set yrange [\""+graphOffset+"\":]");
|
||||||
|
|
||||||
appendfln(result, "set ylabel \"%s\"", settings.getYlabel());
|
appendfln(result, "set ylabel \"%s\"", settings.getYlabel());
|
||||||
switch (settings.getYAxisScale()) {
|
switch (settings.getYAxisScale()) {
|
||||||
case LINEAR:
|
case LINEAR:
|
||||||
break;
|
break;
|
||||||
case LOG10:
|
case LOG10:
|
||||||
appendfln(result, "set logscale y");
|
appendfln(result, "set logscale y");
|
||||||
break;
|
break;
|
||||||
case LOG2:
|
case LOG2:
|
||||||
appendfln(result, "set logscale y 2");
|
appendfln(result, "set logscale y 2");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
appendfln(result, "set grid");
|
appendfln(result, "set grid");
|
||||||
appendfln(result, "set output \"%s\"", settings.getOutput().toAbsolutePath().toString().replace("\\", "/"));
|
appendfln(result, "set output \"%s\"", settings.getOutput().toAbsolutePath().toString().replace("\\", "/"));
|
||||||
|
|
||||||
// marker lines that show which area will be zoomed
|
// marker lines that show which area will be zoomed
|
||||||
final long minDate = Long.parseLong(settings.getDateFrom());
|
final long minDate = Long.parseLong(settings.getDateFrom());
|
||||||
final long maxDate = Long.parseLong(settings.getDateTo());
|
final long maxDate = Long.parseLong(settings.getDateTo());
|
||||||
appendfln(result, "set arrow from "+(minDate + (maxDate-minDate)*0.25)+","+graphOffset+" rto graph 0,1 lt 3 lc rgb \"#EEEEEE\" nohead");
|
appendfln(result, "set arrow from "+(minDate + (maxDate-minDate)*0.25)+","+graphOffset+" rto graph 0,1 lt 3 lc rgb \"#EEEEEE\" nohead");
|
||||||
appendfln(result, "set arrow from "+(minDate + (maxDate-minDate)*0.75)+","+graphOffset+" rto graph 0,1 lc rgb \"#EEEEEE\" nohead");
|
appendfln(result, "set arrow from "+(minDate + (maxDate-minDate)*0.75)+","+graphOffset+" rto graph 0,1 lc rgb \"#EEEEEE\" nohead");
|
||||||
|
|
||||||
appendf(result, "plot ");
|
if (settings.isKeyOutside()){
|
||||||
|
appendfln(result, "set key outside");
|
||||||
count = 1;
|
}
|
||||||
for (final DataSeries dataSerie : dataSeries) {
|
appendfln(result, "set key font \",10\"");
|
||||||
appendfln(result, "'%s' using 1:2 title '%s' with points, \\", dataSerie.getDataFile(),
|
|
||||||
dataSerie.getTitle());
|
appendf(result, "plot ");
|
||||||
if (settings.getAggregate() == AggreateInternal.MEAN) {
|
|
||||||
appendfln(result, "A%d_mean title '%s Mean', \\", count, dataSerie.getTitle(),
|
count = 1;
|
||||||
dataSerie.getTitle());
|
for (final DataSeries dataSerie : dataSeries) {
|
||||||
}
|
appendfln(result, "'%s' using 1:2 title '%s' with points, \\", dataSerie.getDataFile(),
|
||||||
count++;
|
dataSerie.getTitle());
|
||||||
}
|
if (settings.getAggregate() == AggreateInternal.MEAN) {
|
||||||
|
appendfln(result, "A%d_mean title '%s Mean', \\", count, dataSerie.getTitle(),
|
||||||
return result.toString();
|
dataSerie.getTitle());
|
||||||
}
|
}
|
||||||
|
count++;
|
||||||
private void appendfln(final StringBuilder builder, final String format, final Object... args) {
|
}
|
||||||
builder.append(String.format(format + "\n", args));
|
|
||||||
}
|
return result.toString();
|
||||||
|
}
|
||||||
private void appendf(final StringBuilder builder, final String format, final Object... args) {
|
|
||||||
builder.append(String.format(format, args));
|
private void appendfln(final StringBuilder builder, final String format, final Object... args) {
|
||||||
}
|
builder.append(String.format(format + "\n", args));
|
||||||
|
}
|
||||||
}
|
|
||||||
|
private void appendf(final StringBuilder builder, final String format, final Object... args) {
|
||||||
|
builder.append(String.format(format, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,150 +1,159 @@
|
|||||||
package org.lucares.recommind.logs;
|
package org.lucares.recommind.logs;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import org.lucares.pdb.plot.api.AggreateInternal;
|
import org.lucares.pdb.plot.api.AggreateInternal;
|
||||||
import org.lucares.pdb.plot.api.AxisScale;
|
import org.lucares.pdb.plot.api.AxisScale;
|
||||||
|
|
||||||
public class GnuplotSettings {
|
public class GnuplotSettings {
|
||||||
private String terminal = "png";
|
private String terminal = "png";
|
||||||
private int height = 1200;
|
private int height = 1200;
|
||||||
private int width = 1600;
|
private int width = 1600;
|
||||||
private String timefmt = "%s"; //"%Y-%m-%dT%H:%M:%S"; // TODO @ahr timefmt
|
private String timefmt = "%s"; //"%Y-%m-%dT%H:%M:%S"; // TODO @ahr timefmt
|
||||||
|
|
||||||
// set format for x-axis
|
// set format for x-axis
|
||||||
private String formatX = "%Y-%m-%d %H:%M:%S";
|
private String formatX = "%Y-%m-%d %H:%M:%S";
|
||||||
|
|
||||||
// set datafile separator
|
// set datafile separator
|
||||||
private String datafileSeparator = ",";
|
private String datafileSeparator = ",";
|
||||||
|
|
||||||
// set xlabel
|
// set xlabel
|
||||||
private String xlabel = "Time";
|
private String xlabel = "Time";
|
||||||
|
|
||||||
// set ylabel
|
// set ylabel
|
||||||
private String ylabel = "Duration in ms";
|
private String ylabel = "Duration in ms";
|
||||||
|
|
||||||
// set output "datausage.png"
|
// set output "datausage.png"
|
||||||
private final Path output;
|
private final Path output;
|
||||||
|
|
||||||
// set xtics rotate by 10 degree
|
// set xtics rotate by 10 degree
|
||||||
private int rotateXAxisLabel = -10;
|
private int rotateXAxisLabel = -10;
|
||||||
private AxisScale yAxisScale;
|
private AxisScale yAxisScale;
|
||||||
private String dateFrom;
|
private String dateFrom;
|
||||||
private String dateTo;
|
private String dateTo;
|
||||||
private AggreateInternal aggregate;
|
private AggreateInternal aggregate;
|
||||||
|
private boolean keyOutside = false;
|
||||||
public GnuplotSettings(final Path output) {
|
|
||||||
this.output = output;
|
public GnuplotSettings(final Path output) {
|
||||||
}
|
this.output = output;
|
||||||
|
}
|
||||||
public int getRotateXAxisLabel() {
|
|
||||||
return rotateXAxisLabel;
|
public int getRotateXAxisLabel() {
|
||||||
}
|
return rotateXAxisLabel;
|
||||||
|
}
|
||||||
public void setRotateXAxisLabel(final int rotateXAxisLabel) {
|
|
||||||
this.rotateXAxisLabel = rotateXAxisLabel;
|
public void setRotateXAxisLabel(final int rotateXAxisLabel) {
|
||||||
}
|
this.rotateXAxisLabel = rotateXAxisLabel;
|
||||||
|
}
|
||||||
public String getTerminal() {
|
|
||||||
return terminal;
|
public String getTerminal() {
|
||||||
}
|
return terminal;
|
||||||
|
}
|
||||||
public void setTerminal(final String terminal) {
|
|
||||||
this.terminal = terminal;
|
public void setTerminal(final String terminal) {
|
||||||
}
|
this.terminal = terminal;
|
||||||
|
}
|
||||||
public int getHeight() {
|
|
||||||
return height;
|
public int getHeight() {
|
||||||
}
|
return height;
|
||||||
|
}
|
||||||
public void setHeight(final int height) {
|
|
||||||
this.height = height;
|
public void setHeight(final int height) {
|
||||||
}
|
this.height = height;
|
||||||
|
}
|
||||||
public int getWidth() {
|
|
||||||
return width;
|
public int getWidth() {
|
||||||
}
|
return width;
|
||||||
|
}
|
||||||
public void setWidth(final int width) {
|
|
||||||
this.width = width;
|
public void setWidth(final int width) {
|
||||||
}
|
this.width = width;
|
||||||
|
}
|
||||||
public String getTimefmt() {
|
|
||||||
return timefmt;
|
public String getTimefmt() {
|
||||||
}
|
return timefmt;
|
||||||
|
}
|
||||||
public void setTimefmt(final String timefmt) {
|
|
||||||
this.timefmt = timefmt;
|
public void setTimefmt(final String timefmt) {
|
||||||
}
|
this.timefmt = timefmt;
|
||||||
|
}
|
||||||
public String getFormatX() {
|
|
||||||
return formatX;
|
public String getFormatX() {
|
||||||
}
|
return formatX;
|
||||||
|
}
|
||||||
public void setFormatX(final String formatX) {
|
|
||||||
this.formatX = formatX;
|
public void setFormatX(final String formatX) {
|
||||||
}
|
this.formatX = formatX;
|
||||||
|
}
|
||||||
public String getDatafileSeparator() {
|
|
||||||
return datafileSeparator;
|
public String getDatafileSeparator() {
|
||||||
}
|
return datafileSeparator;
|
||||||
|
}
|
||||||
public void setDatafileSeparator(final String datafileSeparator) {
|
|
||||||
this.datafileSeparator = datafileSeparator;
|
public void setDatafileSeparator(final String datafileSeparator) {
|
||||||
}
|
this.datafileSeparator = datafileSeparator;
|
||||||
|
}
|
||||||
public String getXlabel() {
|
|
||||||
return xlabel;
|
public String getXlabel() {
|
||||||
}
|
return xlabel;
|
||||||
|
}
|
||||||
public void setXlabel(final String xlabel) {
|
|
||||||
this.xlabel = xlabel;
|
public void setXlabel(final String xlabel) {
|
||||||
}
|
this.xlabel = xlabel;
|
||||||
|
}
|
||||||
public String getYlabel() {
|
|
||||||
return ylabel;
|
public String getYlabel() {
|
||||||
}
|
return ylabel;
|
||||||
|
}
|
||||||
public void setYlabel(final String ylabel) {
|
|
||||||
this.ylabel = ylabel;
|
public void setYlabel(final String ylabel) {
|
||||||
}
|
this.ylabel = ylabel;
|
||||||
|
}
|
||||||
public Path getOutput() {
|
|
||||||
return output;
|
public Path getOutput() {
|
||||||
}
|
return output;
|
||||||
|
}
|
||||||
public void setYAxisScale(final AxisScale yAxisScale) {
|
|
||||||
this.yAxisScale = yAxisScale;
|
public void setYAxisScale(final AxisScale yAxisScale) {
|
||||||
}
|
this.yAxisScale = yAxisScale;
|
||||||
|
}
|
||||||
public AxisScale getYAxisScale() {
|
|
||||||
return yAxisScale;
|
public AxisScale getYAxisScale() {
|
||||||
}
|
return yAxisScale;
|
||||||
|
}
|
||||||
public void setDateFrom(final String dateFrom) {
|
|
||||||
this.dateFrom = dateFrom;
|
public void setDateFrom(final String dateFrom) {
|
||||||
}
|
this.dateFrom = dateFrom;
|
||||||
|
}
|
||||||
public String getDateFrom() {
|
|
||||||
return dateFrom;
|
public String getDateFrom() {
|
||||||
}
|
return dateFrom;
|
||||||
|
}
|
||||||
public void setDateTo(final String dateTo) {
|
|
||||||
this.dateTo = dateTo;
|
public void setDateTo(final String dateTo) {
|
||||||
}
|
this.dateTo = dateTo;
|
||||||
|
}
|
||||||
public String getDateTo() {
|
|
||||||
return dateTo;
|
public String getDateTo() {
|
||||||
}
|
return dateTo;
|
||||||
|
}
|
||||||
public void setAggregate(AggreateInternal aggregate) {
|
|
||||||
this.aggregate = aggregate;
|
public void setAggregate(AggreateInternal aggregate) {
|
||||||
}
|
this.aggregate = aggregate;
|
||||||
|
}
|
||||||
public AggreateInternal getAggregate() {
|
|
||||||
return aggregate;
|
public AggreateInternal getAggregate() {
|
||||||
}
|
return aggregate;
|
||||||
|
}
|
||||||
// plot 'sample.txt' using 1:2 title 'Bytes' with linespoints 2
|
|
||||||
|
public void setKeyOutside(boolean keyOutside) {
|
||||||
}
|
this.keyOutside = keyOutside;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isKeyOutside() {
|
||||||
|
return keyOutside;
|
||||||
|
}
|
||||||
|
|
||||||
|
// plot 'sample.txt' using 1:2 title 'Bytes' with linespoints 2
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,284 +1,285 @@
|
|||||||
package org.lucares.recommind.logs;
|
package org.lucares.recommind.logs;
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.LinkOption;
|
import java.nio.file.LinkOption;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.lucares.pdb.api.Entry;
|
import org.lucares.pdb.api.Entry;
|
||||||
import org.lucares.pdb.api.GroupResult;
|
import org.lucares.pdb.api.GroupResult;
|
||||||
import org.lucares.pdb.api.Result;
|
import org.lucares.pdb.api.Result;
|
||||||
import org.lucares.pdb.api.Tags;
|
import org.lucares.pdb.api.Tags;
|
||||||
import org.lucares.pdb.plot.api.Limit;
|
import org.lucares.pdb.plot.api.Limit;
|
||||||
import org.lucares.pdb.plot.api.PlotSettings;
|
import org.lucares.pdb.plot.api.PlotSettings;
|
||||||
import org.lucares.performance.db.PerformanceDb;
|
import org.lucares.performance.db.PerformanceDb;
|
||||||
import org.lucares.utils.file.FileUtils;
|
import org.lucares.utils.file.FileUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class Plotter {
|
public class Plotter {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(Plotter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(Plotter.class);
|
||||||
private static final Logger METRICS_LOGGER = LoggerFactory.getLogger("org.lucares.metrics.plotter");
|
private static final Logger METRICS_LOGGER = LoggerFactory.getLogger("org.lucares.metrics.plotter");
|
||||||
|
|
||||||
private static final String DEFAULT_GROUP = "<none>";
|
private static final String DEFAULT_GROUP = "<none>";
|
||||||
|
|
||||||
private static final int INT_TO_STRING_CACHE_SIZE= 1000;
|
private static final int INT_TO_STRING_CACHE_SIZE= 1000;
|
||||||
private static final String[] INT_TO_STRING;
|
private static final String[] INT_TO_STRING;
|
||||||
static {
|
static {
|
||||||
|
|
||||||
INT_TO_STRING = new String[INT_TO_STRING_CACHE_SIZE];
|
INT_TO_STRING = new String[INT_TO_STRING_CACHE_SIZE];
|
||||||
|
|
||||||
for (int i = 0; i < INT_TO_STRING_CACHE_SIZE; i++){
|
for (int i = 0; i < INT_TO_STRING_CACHE_SIZE; i++){
|
||||||
INT_TO_STRING[i] = String.valueOf(i);
|
INT_TO_STRING[i] = String.valueOf(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PerformanceDb db;
|
private final PerformanceDb db;
|
||||||
private final Path tmpBaseDir;
|
private final Path tmpBaseDir;
|
||||||
private final Path outputDir;
|
private final Path outputDir;
|
||||||
|
|
||||||
public Plotter(final PerformanceDb db, final Path tmpBaseDir, final Path outputDir) {
|
public Plotter(final PerformanceDb db, final Path tmpBaseDir, final Path outputDir) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.tmpBaseDir = tmpBaseDir;
|
this.tmpBaseDir = tmpBaseDir;
|
||||||
this.outputDir = outputDir;
|
this.outputDir = outputDir;
|
||||||
|
|
||||||
if (!Files.isDirectory(tmpBaseDir, LinkOption.NOFOLLOW_LINKS)) {
|
if (!Files.isDirectory(tmpBaseDir, LinkOption.NOFOLLOW_LINKS)) {
|
||||||
throw new IllegalArgumentException(tmpBaseDir + " is not a directory");
|
throw new IllegalArgumentException(tmpBaseDir + " is not a directory");
|
||||||
}
|
}
|
||||||
if (!Files.isDirectory(outputDir)) {
|
if (!Files.isDirectory(outputDir)) {
|
||||||
throw new IllegalArgumentException(outputDir + " is not a directory");
|
throw new IllegalArgumentException(outputDir + " is not a directory");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getOutputDir() {
|
public Path getOutputDir() {
|
||||||
return outputDir;
|
return outputDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlotResult plot(final PlotSettings plotSettings) throws InternalPlottingException {
|
public PlotResult plot(final PlotSettings plotSettings) throws InternalPlottingException {
|
||||||
|
|
||||||
LOGGER.trace("start plot: {}", plotSettings);
|
LOGGER.trace("start plot: {}", plotSettings);
|
||||||
|
|
||||||
final String tmpSubDir = uniqueDirectoryName();
|
final String tmpSubDir = uniqueDirectoryName();
|
||||||
final Path tmpDir = tmpBaseDir.resolve(tmpSubDir);
|
final Path tmpDir = tmpBaseDir.resolve(tmpSubDir);
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(tmpDir);
|
Files.createDirectories(tmpDir);
|
||||||
final List<DataSeries> dataSeries = new ArrayList<>();
|
final List<DataSeries> dataSeries = new ArrayList<>();
|
||||||
|
|
||||||
final String query = plotSettings.getQuery();
|
final String query = plotSettings.getQuery();
|
||||||
final List<String> groupBy = plotSettings.getGroupBy();
|
final List<String> groupBy = plotSettings.getGroupBy();
|
||||||
final int height = plotSettings.getHeight();
|
final int height = plotSettings.getHeight();
|
||||||
final int width = plotSettings.getWidth();
|
final int width = plotSettings.getWidth();
|
||||||
final OffsetDateTime dateFrom = plotSettings.dateFrom();
|
final OffsetDateTime dateFrom = plotSettings.dateFrom();
|
||||||
final OffsetDateTime dateTo = plotSettings.dateTo();
|
final OffsetDateTime dateTo = plotSettings.dateTo();
|
||||||
|
|
||||||
final Result result = db.get(query, groupBy);
|
final Result result = db.get(query, groupBy);
|
||||||
|
|
||||||
for (final GroupResult groupResult : result.getGroups()) {
|
for (final GroupResult groupResult : result.getGroups()) {
|
||||||
|
|
||||||
final Stream<Entry> entries = groupResult.asStream();
|
final Stream<Entry> entries = groupResult.asStream();
|
||||||
|
|
||||||
final File dataFile = File.createTempFile("data", ".dat", tmpDir.toFile());
|
final File dataFile = File.createTempFile("data", ".dat", tmpDir.toFile());
|
||||||
final CsvSummary csvSummary = toCsv(entries, dataFile, dateFrom, dateTo);
|
final CsvSummary csvSummary = toCsv(entries, dataFile, dateFrom, dateTo);
|
||||||
|
|
||||||
final String title = title(groupResult.getGroupedBy(), csvSummary.getValues());
|
final String title = title(groupResult.getGroupedBy(), csvSummary.getValues());
|
||||||
final DataSeries dataSerie = new DataSeries(dataFile, title, csvSummary.getValues(), csvSummary.getMaxValue());
|
final DataSeries dataSerie = new DataSeries(dataFile, title, csvSummary.getValues(), csvSummary.getMaxValue());
|
||||||
if (dataSerie.getValues() > 0) {
|
if (dataSerie.getValues() > 0) {
|
||||||
dataSeries.add(dataSerie);
|
dataSeries.add(dataSerie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataSeries.isEmpty()) {
|
if (dataSeries.isEmpty()) {
|
||||||
throw new NoDataPointsException();
|
throw new NoDataPointsException();
|
||||||
}
|
}
|
||||||
|
|
||||||
sortAndLimit(dataSeries, plotSettings);
|
sortAndLimit(dataSeries, plotSettings);
|
||||||
|
|
||||||
final Path outputFile = Files.createTempFile(outputDir, "out", ".png");
|
final Path outputFile = Files.createTempFile(outputDir, "out", ".png");
|
||||||
final Gnuplot gnuplot = new Gnuplot(tmpBaseDir);
|
final Gnuplot gnuplot = new Gnuplot(tmpBaseDir);
|
||||||
final GnuplotSettings gnuplotSettings = new GnuplotSettings(outputFile);
|
final GnuplotSettings gnuplotSettings = new GnuplotSettings(outputFile);
|
||||||
gnuplotSettings.setHeight(height);
|
gnuplotSettings.setHeight(height);
|
||||||
gnuplotSettings.setWidth(width);
|
gnuplotSettings.setWidth(width);
|
||||||
defineXAxis(gnuplotSettings, plotSettings.dateFrom(), plotSettings.dateTo());
|
defineXAxis(gnuplotSettings, plotSettings.dateFrom(), plotSettings.dateTo());
|
||||||
|
|
||||||
gnuplotSettings.setYAxisScale(plotSettings.getYAxisScale());
|
gnuplotSettings.setYAxisScale(plotSettings.getYAxisScale());
|
||||||
gnuplotSettings.setAggregate(plotSettings.getAggregate());
|
gnuplotSettings.setAggregate(plotSettings.getAggregate());
|
||||||
gnuplot.plot(gnuplotSettings, dataSeries);
|
gnuplotSettings.setKeyOutside(plotSettings.isKeyOutside());
|
||||||
|
gnuplot.plot(gnuplotSettings, dataSeries);
|
||||||
return new PlotResult(outputFile.getFileName(), dataSeries);
|
|
||||||
} catch (final InterruptedException e) {
|
return new PlotResult(outputFile.getFileName(), dataSeries);
|
||||||
Thread.currentThread().interrupt();
|
} catch (final InterruptedException e) {
|
||||||
throw new IllegalStateException("Plotting was interrupted.");
|
Thread.currentThread().interrupt();
|
||||||
} catch (final IOException e) {
|
throw new IllegalStateException("Plotting was interrupted.");
|
||||||
throw new InternalPlottingException("Plotting failed: " + e.getMessage(), e);
|
} catch (final IOException e) {
|
||||||
} finally {
|
throw new InternalPlottingException("Plotting failed: " + e.getMessage(), e);
|
||||||
FileUtils.delete(tmpDir);
|
} finally {
|
||||||
LOGGER.trace("done plot");
|
FileUtils.delete(tmpDir);
|
||||||
}
|
LOGGER.trace("done plot");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private void defineXAxis(final GnuplotSettings gnuplotSettings, final OffsetDateTime minDate,
|
|
||||||
final OffsetDateTime maxDate) {
|
private void defineXAxis(final GnuplotSettings gnuplotSettings, final OffsetDateTime minDate,
|
||||||
|
final OffsetDateTime maxDate) {
|
||||||
String formatX;
|
|
||||||
int rotateX;
|
String formatX;
|
||||||
String formattedMinDate;
|
int rotateX;
|
||||||
String formattedMaxDate;
|
String formattedMinDate;
|
||||||
if (minDate.until(maxDate, ChronoUnit.WEEKS) > 1) {
|
String formattedMaxDate;
|
||||||
formatX = "%Y-%m-%d";
|
if (minDate.until(maxDate, ChronoUnit.WEEKS) > 1) {
|
||||||
rotateX = 0;
|
formatX = "%Y-%m-%d";
|
||||||
} else if (minDate.until(maxDate, ChronoUnit.SECONDS) > 30) {
|
rotateX = 0;
|
||||||
formatX = "%Y-%m-%d %H:%M:%S";
|
} else if (minDate.until(maxDate, ChronoUnit.SECONDS) > 30) {
|
||||||
rotateX = gnuplotSettings.getRotateXAxisLabel();
|
formatX = "%Y-%m-%d %H:%M:%S";
|
||||||
} else {
|
rotateX = gnuplotSettings.getRotateXAxisLabel();
|
||||||
formatX = "%Y-%m-%d %H:%M:%.3S";
|
} else {
|
||||||
rotateX = gnuplotSettings.getRotateXAxisLabel();
|
formatX = "%Y-%m-%d %H:%M:%.3S";
|
||||||
}
|
rotateX = gnuplotSettings.getRotateXAxisLabel();
|
||||||
formattedMinDate = String.valueOf(minDate.toEpochSecond());
|
}
|
||||||
formattedMaxDate = String.valueOf(maxDate.toEpochSecond());
|
formattedMinDate = String.valueOf(minDate.toEpochSecond());
|
||||||
|
formattedMaxDate = String.valueOf(maxDate.toEpochSecond());
|
||||||
gnuplotSettings.setFormatX(formatX);
|
|
||||||
gnuplotSettings.setRotateXAxisLabel(rotateX);
|
gnuplotSettings.setFormatX(formatX);
|
||||||
gnuplotSettings.setDateFrom(formattedMinDate);
|
gnuplotSettings.setRotateXAxisLabel(rotateX);
|
||||||
gnuplotSettings.setDateTo(formattedMaxDate);
|
gnuplotSettings.setDateFrom(formattedMinDate);
|
||||||
}
|
gnuplotSettings.setDateTo(formattedMaxDate);
|
||||||
|
}
|
||||||
private String uniqueDirectoryName() {
|
|
||||||
return OffsetDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm_ss")) + "_"
|
private String uniqueDirectoryName() {
|
||||||
+ UUID.randomUUID().toString();
|
return OffsetDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm_ss")) + "_"
|
||||||
}
|
+ UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
private void sortAndLimit(final List<DataSeries> dataSeries, final PlotSettings plotSettings) {
|
|
||||||
|
private void sortAndLimit(final List<DataSeries> dataSeries, final PlotSettings plotSettings) {
|
||||||
final Limit limitBy = plotSettings.getLimitBy();
|
|
||||||
dataSeries.sort(getDataSeriesComparator(limitBy));
|
final Limit limitBy = plotSettings.getLimitBy();
|
||||||
|
dataSeries.sort(getDataSeriesComparator(limitBy));
|
||||||
switch (limitBy) {
|
|
||||||
case FEWEST_VALUES:
|
switch (limitBy) {
|
||||||
case MOST_VALUES:
|
case FEWEST_VALUES:
|
||||||
case MAX_VALUE:
|
case MOST_VALUES:
|
||||||
case MIN_VALUE:
|
case MAX_VALUE:
|
||||||
while (dataSeries.size() > plotSettings.getLimit()) {
|
case MIN_VALUE:
|
||||||
dataSeries.remove(plotSettings.getLimit());
|
while (dataSeries.size() > plotSettings.getLimit()) {
|
||||||
}
|
dataSeries.remove(plotSettings.getLimit());
|
||||||
break;
|
}
|
||||||
case NO_LIMIT:
|
break;
|
||||||
}
|
case NO_LIMIT:
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private Comparator<? super DataSeries> getDataSeriesComparator(final Limit limitBy) {
|
|
||||||
|
private Comparator<? super DataSeries> getDataSeriesComparator(final Limit limitBy) {
|
||||||
switch (limitBy) {
|
|
||||||
case MOST_VALUES:
|
switch (limitBy) {
|
||||||
return DataSeries.BY_NUMBER_OF_VALUES.reversed();
|
case MOST_VALUES:
|
||||||
case FEWEST_VALUES:
|
return DataSeries.BY_NUMBER_OF_VALUES.reversed();
|
||||||
return DataSeries.BY_NUMBER_OF_VALUES;
|
case FEWEST_VALUES:
|
||||||
case MAX_VALUE:
|
return DataSeries.BY_NUMBER_OF_VALUES;
|
||||||
return DataSeries.BY_MAX_VALUE.reversed();
|
case MAX_VALUE:
|
||||||
case MIN_VALUE:
|
return DataSeries.BY_MAX_VALUE.reversed();
|
||||||
return DataSeries.BY_MAX_VALUE;
|
case MIN_VALUE:
|
||||||
case NO_LIMIT:
|
return DataSeries.BY_MAX_VALUE;
|
||||||
return DataSeries.BY_NUMBER_OF_VALUES;
|
case NO_LIMIT:
|
||||||
}
|
return DataSeries.BY_NUMBER_OF_VALUES;
|
||||||
throw new IllegalStateException("unhandled enum: "+ limitBy);
|
}
|
||||||
}
|
throw new IllegalStateException("unhandled enum: "+ limitBy);
|
||||||
|
}
|
||||||
private String title(final Tags tags, final int values) {
|
|
||||||
|
private String title(final Tags tags, final int values) {
|
||||||
final StringBuilder result = new StringBuilder();
|
|
||||||
|
final StringBuilder result = new StringBuilder();
|
||||||
if (tags.isEmpty()) {
|
|
||||||
result.append(DEFAULT_GROUP);
|
if (tags.isEmpty()) {
|
||||||
} else {
|
result.append(DEFAULT_GROUP);
|
||||||
tags.forEach((k, v) -> {
|
} else {
|
||||||
if (result.length() > 0) {
|
tags.forEach((k, v) -> {
|
||||||
result.append(" / ");
|
if (result.length() > 0) {
|
||||||
}
|
result.append(" / ");
|
||||||
result.append(v);
|
}
|
||||||
});
|
result.append(v);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
result.append(" (");
|
|
||||||
result.append(values);
|
result.append(" (");
|
||||||
result.append(")");
|
result.append(values);
|
||||||
|
result.append(")");
|
||||||
return result.toString();
|
|
||||||
|
return result.toString();
|
||||||
}
|
|
||||||
|
}
|
||||||
private static CsvSummary toCsv(final Stream<Entry> entries, final File dataFile, final OffsetDateTime dateFrom,
|
|
||||||
final OffsetDateTime dateTo) throws IOException {
|
private static CsvSummary toCsv(final Stream<Entry> entries, final File dataFile, final OffsetDateTime dateFrom,
|
||||||
|
final OffsetDateTime dateTo) throws IOException {
|
||||||
final long start = System.nanoTime();
|
|
||||||
int count = 0;
|
final long start = System.nanoTime();
|
||||||
final long fromEpochMilli = dateFrom.toInstant().toEpochMilli();
|
int count = 0;
|
||||||
final long toEpochMilli = dateTo.toInstant().toEpochMilli();
|
final long fromEpochMilli = dateFrom.toInstant().toEpochMilli();
|
||||||
final boolean useMillis = (toEpochMilli - fromEpochMilli) < TimeUnit.MINUTES.toMillis(5);
|
final long toEpochMilli = dateTo.toInstant().toEpochMilli();
|
||||||
|
final boolean useMillis = (toEpochMilli - fromEpochMilli) < TimeUnit.MINUTES.toMillis(5);
|
||||||
long maxValue = 0;
|
|
||||||
long ignoredValues = 0;
|
long maxValue = 0;
|
||||||
final int separator = ',';
|
long ignoredValues = 0;
|
||||||
final int newline = '\n';
|
final int separator = ',';
|
||||||
final StringBuilder formattedDateBuilder = new StringBuilder();
|
final int newline = '\n';
|
||||||
try (final Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dataFile), StandardCharsets.US_ASCII));
|
final StringBuilder formattedDateBuilder = new StringBuilder();
|
||||||
final Formatter formatter = new Formatter(formattedDateBuilder);) {
|
try (final Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dataFile), StandardCharsets.US_ASCII));
|
||||||
|
final Formatter formatter = new Formatter(formattedDateBuilder);) {
|
||||||
final Iterator<Entry> it = entries.iterator();
|
|
||||||
while (it.hasNext()) {
|
final Iterator<Entry> it = entries.iterator();
|
||||||
final Entry entry = it.next();
|
while (it.hasNext()) {
|
||||||
|
final Entry entry = it.next();
|
||||||
if (fromEpochMilli <= entry.getEpochMilli() && entry.getEpochMilli() <= toEpochMilli) {
|
|
||||||
|
if (fromEpochMilli <= entry.getEpochMilli() && entry.getEpochMilli() <= toEpochMilli) {
|
||||||
final String value = longToString(entry.getValue());
|
|
||||||
final String formattedDate;
|
final String value = longToString(entry.getValue());
|
||||||
|
final String formattedDate;
|
||||||
if (useMillis){
|
|
||||||
formattedDateBuilder.delete(0, formattedDateBuilder.length());
|
if (useMillis){
|
||||||
formatter.format("%.3f", entry.getEpochMilli() / 1000.0);
|
formattedDateBuilder.delete(0, formattedDateBuilder.length());
|
||||||
formattedDate = formattedDateBuilder.toString();
|
formatter.format("%.3f", entry.getEpochMilli() / 1000.0);
|
||||||
}else {
|
formattedDate = formattedDateBuilder.toString();
|
||||||
formattedDate = String.valueOf(entry.getEpochMilli() / 1000);
|
}else {
|
||||||
}
|
formattedDate = String.valueOf(entry.getEpochMilli() / 1000);
|
||||||
|
}
|
||||||
output.write(formattedDate);
|
|
||||||
output.write(separator);
|
output.write(formattedDate);
|
||||||
output.write(value);
|
output.write(separator);
|
||||||
output.write(newline);
|
output.write(value);
|
||||||
|
output.write(newline);
|
||||||
count++;
|
|
||||||
maxValue = Math.max(maxValue, entry.getValue());
|
count++;
|
||||||
}else {
|
maxValue = Math.max(maxValue, entry.getValue());
|
||||||
ignoredValues++;
|
}else {
|
||||||
}
|
ignoredValues++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
METRICS_LOGGER.debug("wrote {} values to csv in: {}ms (ignored {} values) use millis: {}", count, (System.nanoTime() - start) / 1_000_000.0, ignoredValues, Boolean.toString(useMillis));
|
|
||||||
return new CsvSummary(count, maxValue);
|
METRICS_LOGGER.debug("wrote {} values to csv in: {}ms (ignored {} values) use millis: {}", count, (System.nanoTime() - start) / 1_000_000.0, ignoredValues, Boolean.toString(useMillis));
|
||||||
}
|
return new CsvSummary(count, maxValue);
|
||||||
|
}
|
||||||
private static String longToString(final long value){
|
|
||||||
// using pre-generated strings reduces memory allocation by up to 25%
|
private static String longToString(final long value){
|
||||||
|
// using pre-generated strings reduces memory allocation by up to 25%
|
||||||
if (value < INT_TO_STRING_CACHE_SIZE){
|
|
||||||
return INT_TO_STRING[(int) value];
|
if (value < INT_TO_STRING_CACHE_SIZE){
|
||||||
}
|
return INT_TO_STRING[(int) value];
|
||||||
return String.valueOf(value);
|
}
|
||||||
}
|
return String.valueOf(value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,68 +1,69 @@
|
|||||||
package org.lucares.pdbui;
|
package org.lucares.pdbui;
|
||||||
|
|
||||||
import org.lucares.pdb.plot.api.AggreateInternal;
|
import org.lucares.pdb.plot.api.AggreateInternal;
|
||||||
import org.lucares.pdb.plot.api.AxisScale;
|
import org.lucares.pdb.plot.api.AxisScale;
|
||||||
import org.lucares.pdb.plot.api.Limit;
|
import org.lucares.pdb.plot.api.Limit;
|
||||||
import org.lucares.pdb.plot.api.PlotSettings;
|
import org.lucares.pdb.plot.api.PlotSettings;
|
||||||
import org.lucares.pdbui.domain.Aggregate;
|
import org.lucares.pdbui.domain.Aggregate;
|
||||||
import org.lucares.pdbui.domain.LimitBy;
|
import org.lucares.pdbui.domain.LimitBy;
|
||||||
import org.lucares.pdbui.domain.PlotRequest;
|
import org.lucares.pdbui.domain.PlotRequest;
|
||||||
import org.lucares.pdbui.domain.YAxis;
|
import org.lucares.pdbui.domain.YAxis;
|
||||||
|
|
||||||
class PlotSettingsTransformer {
|
class PlotSettingsTransformer {
|
||||||
static PlotSettings toSettings(final PlotRequest request) {
|
static PlotSettings toSettings(final PlotRequest request) {
|
||||||
|
|
||||||
final PlotSettings result = new PlotSettings();
|
final PlotSettings result = new PlotSettings();
|
||||||
|
|
||||||
result.setQuery(request.getQuery());
|
result.setQuery(request.getQuery());
|
||||||
result.setGroupBy(request.getGroupBy());
|
result.setGroupBy(request.getGroupBy());
|
||||||
result.setHeight(request.getHeight());
|
result.setHeight(request.getHeight());
|
||||||
result.setWidth(request.getWidth());
|
result.setWidth(request.getWidth());
|
||||||
result.setLimit(request.getLimit());
|
result.setLimit(request.getLimit());
|
||||||
result.setLimitBy(toLimit(request.getLimitBy()));
|
result.setLimitBy(toLimit(request.getLimitBy()));
|
||||||
result.setDateFrom(request.getDateFrom());
|
result.setDateFrom(request.getDateFrom());
|
||||||
result.setDateRange(request.getDateRange());
|
result.setDateRange(request.getDateRange());
|
||||||
result.setYAxisScale(toAxisScale(request.getAxisScale()));
|
result.setYAxisScale(toAxisScale(request.getAxisScale()));
|
||||||
result.setAggregate(toAggregateInternal(request.getAggregate()));
|
result.setAggregate(toAggregateInternal(request.getAggregate()));
|
||||||
|
result.setKeyOutside(request.isKeyOutside());
|
||||||
return result;
|
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
private static AggreateInternal toAggregateInternal(Aggregate aggregate) {
|
|
||||||
switch (aggregate) {
|
private static AggreateInternal toAggregateInternal(Aggregate aggregate) {
|
||||||
case NONE:return AggreateInternal.NONE;
|
switch (aggregate) {
|
||||||
case MEAN:return AggreateInternal.MEAN;
|
case NONE:return AggreateInternal.NONE;
|
||||||
}
|
case MEAN:return AggreateInternal.MEAN;
|
||||||
throw new IllegalStateException("unhandled enum: " + aggregate);
|
}
|
||||||
}
|
throw new IllegalStateException("unhandled enum: " + aggregate);
|
||||||
|
}
|
||||||
private static AxisScale toAxisScale(final YAxis yAxis) {
|
|
||||||
switch (yAxis) {
|
private static AxisScale toAxisScale(final YAxis yAxis) {
|
||||||
case LINEAR:
|
switch (yAxis) {
|
||||||
return AxisScale.LINEAR;
|
case LINEAR:
|
||||||
case LOG10:
|
return AxisScale.LINEAR;
|
||||||
return AxisScale.LOG10;
|
case LOG10:
|
||||||
case LOG2:
|
return AxisScale.LOG10;
|
||||||
return AxisScale.LOG2;
|
case LOG2:
|
||||||
default:
|
return AxisScale.LOG2;
|
||||||
throw new IllegalStateException("unhandled enum: " + yAxis);
|
default:
|
||||||
}
|
throw new IllegalStateException("unhandled enum: " + yAxis);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static Limit toLimit(final LimitBy limitBy) {
|
|
||||||
switch (limitBy) {
|
private static Limit toLimit(final LimitBy limitBy) {
|
||||||
case NO_LIMIT:
|
switch (limitBy) {
|
||||||
return Limit.NO_LIMIT;
|
case NO_LIMIT:
|
||||||
case FEWEST_VALUES:
|
return Limit.NO_LIMIT;
|
||||||
return Limit.FEWEST_VALUES;
|
case FEWEST_VALUES:
|
||||||
case MOST_VALUES:
|
return Limit.FEWEST_VALUES;
|
||||||
return Limit.MOST_VALUES;
|
case MOST_VALUES:
|
||||||
case MAX_VALUE:
|
return Limit.MOST_VALUES;
|
||||||
return Limit.MAX_VALUE;
|
case MAX_VALUE:
|
||||||
case MIN_VALUE:
|
return Limit.MAX_VALUE;
|
||||||
return Limit.MIN_VALUE;
|
case MIN_VALUE:
|
||||||
default:
|
return Limit.MIN_VALUE;
|
||||||
throw new IllegalStateException("unhandled enum: " + limitBy);
|
default:
|
||||||
}
|
throw new IllegalStateException("unhandled enum: " + limitBy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,113 +1,123 @@
|
|||||||
package org.lucares.pdbui.domain;
|
package org.lucares.pdbui.domain;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PlotRequest {
|
public class PlotRequest {
|
||||||
private String query;
|
private String query;
|
||||||
|
|
||||||
private int height = 1000;
|
private int height = 1000;
|
||||||
|
|
||||||
private int width = 1000;
|
private int width = 1000;
|
||||||
|
|
||||||
private List<String> groupBy;
|
private List<String> groupBy;
|
||||||
|
|
||||||
private LimitBy limitBy = LimitBy.NO_LIMIT;
|
private LimitBy limitBy = LimitBy.NO_LIMIT;
|
||||||
|
|
||||||
private YAxis yAxis = YAxis.LINEAR;
|
private YAxis yAxis = YAxis.LINEAR;
|
||||||
|
|
||||||
private int limit = Integer.MAX_VALUE;
|
private int limit = Integer.MAX_VALUE;
|
||||||
|
|
||||||
private String dateFrom;
|
private String dateFrom;
|
||||||
|
|
||||||
private String dateRange;
|
private String dateRange;
|
||||||
|
|
||||||
private Aggregate aggregate = Aggregate.NONE;
|
private Aggregate aggregate = Aggregate.NONE;
|
||||||
|
|
||||||
public String getQuery() {
|
private boolean keyOutside;
|
||||||
return query;
|
|
||||||
}
|
public String getQuery() {
|
||||||
|
return query;
|
||||||
public void setQuery(final String query) {
|
}
|
||||||
this.query = query;
|
|
||||||
}
|
public void setQuery(final String query) {
|
||||||
|
this.query = query;
|
||||||
public int getWidth() {
|
}
|
||||||
return width;
|
|
||||||
}
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
public void setWidth(final int width) {
|
}
|
||||||
this.width = width;
|
|
||||||
}
|
public void setWidth(final int width) {
|
||||||
|
this.width = width;
|
||||||
public int getHeight() {
|
}
|
||||||
return height;
|
|
||||||
}
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
public void setHeight(final int height) {
|
}
|
||||||
this.height = height;
|
|
||||||
}
|
public void setHeight(final int height) {
|
||||||
|
this.height = height;
|
||||||
@Override
|
}
|
||||||
public String toString() {
|
|
||||||
return query + ":" + height + "x" + width;
|
@Override
|
||||||
}
|
public String toString() {
|
||||||
|
return query + ":" + height + "x" + width;
|
||||||
public List<String> getGroupBy() {
|
}
|
||||||
return groupBy;
|
|
||||||
}
|
public List<String> getGroupBy() {
|
||||||
|
return groupBy;
|
||||||
public void setGroupBy(final List<String> groupBy) {
|
}
|
||||||
this.groupBy = groupBy;
|
|
||||||
}
|
public void setGroupBy(final List<String> groupBy) {
|
||||||
|
this.groupBy = groupBy;
|
||||||
public LimitBy getLimitBy() {
|
}
|
||||||
return limitBy;
|
|
||||||
}
|
public LimitBy getLimitBy() {
|
||||||
|
return limitBy;
|
||||||
public void setLimitBy(final LimitBy limitBy) {
|
}
|
||||||
this.limitBy = limitBy;
|
|
||||||
}
|
public void setLimitBy(final LimitBy limitBy) {
|
||||||
|
this.limitBy = limitBy;
|
||||||
public int getLimit() {
|
}
|
||||||
return limit;
|
|
||||||
}
|
public int getLimit() {
|
||||||
|
return limit;
|
||||||
public void setLimit(final int limit) {
|
}
|
||||||
this.limit = limit;
|
|
||||||
}
|
public void setLimit(final int limit) {
|
||||||
|
this.limit = limit;
|
||||||
public String getDateFrom() {
|
}
|
||||||
return dateFrom;
|
|
||||||
}
|
public String getDateFrom() {
|
||||||
|
return dateFrom;
|
||||||
public void setDateFrom(final String dateFrom) {
|
}
|
||||||
this.dateFrom = dateFrom;
|
|
||||||
}
|
public void setDateFrom(final String dateFrom) {
|
||||||
|
this.dateFrom = dateFrom;
|
||||||
public String getDateRange() {
|
}
|
||||||
return dateRange;
|
|
||||||
}
|
public String getDateRange() {
|
||||||
|
return dateRange;
|
||||||
public void setDateRange(final String dateRange) {
|
}
|
||||||
if (!dateRange.matches("\\d+ (second|minute|hour|day|week|month)s?")) {
|
|
||||||
throw new IllegalArgumentException(dateRange + " is not a valid range");
|
public void setDateRange(final String dateRange) {
|
||||||
}
|
if (!dateRange.matches("\\d+ (second|minute|hour|day|week|month)s?")) {
|
||||||
this.dateRange = dateRange;
|
throw new IllegalArgumentException(dateRange + " is not a valid range");
|
||||||
}
|
}
|
||||||
|
this.dateRange = dateRange;
|
||||||
public YAxis getAxisScale() {
|
}
|
||||||
return yAxis;
|
|
||||||
}
|
public YAxis getAxisScale() {
|
||||||
|
return yAxis;
|
||||||
public void setAxisScale(final YAxis yAxis) {
|
}
|
||||||
this.yAxis = yAxis;
|
|
||||||
}
|
public void setAxisScale(final YAxis yAxis) {
|
||||||
|
this.yAxis = yAxis;
|
||||||
public void setAggregate(Aggregate aggregate) {
|
}
|
||||||
this.aggregate = aggregate;
|
|
||||||
}
|
public void setAggregate(Aggregate aggregate) {
|
||||||
|
this.aggregate = aggregate;
|
||||||
public Aggregate getAggregate() {
|
}
|
||||||
return aggregate;
|
|
||||||
}
|
public Aggregate getAggregate() {
|
||||||
}
|
return aggregate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyOutside(boolean keyOutside) {
|
||||||
|
this.keyOutside = keyOutside;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isKeyOutside() {
|
||||||
|
return keyOutside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,117 +1,125 @@
|
|||||||
html {
|
html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body{
|
body{
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
grid:
|
grid:
|
||||||
"search_field logo" auto
|
"search_field logo" auto
|
||||||
"search logo" auto
|
"search logo" auto
|
||||||
"navigation navigation" auto
|
"navigation navigation" auto
|
||||||
"result result" 1fr
|
"result result" 1fr
|
||||||
/ 1fr auto;
|
/ 1fr auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'FontAwesome';
|
font-family: 'FontAwesome';
|
||||||
src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
|
src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
|
||||||
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
|
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo {
|
.group {
|
||||||
grid-area: logo;
|
display: inline-block;
|
||||||
font-size: 1.2em;
|
}
|
||||||
font-weight: bold;
|
|
||||||
background-color: black;
|
#logo {
|
||||||
color: white;
|
grid-area: logo;
|
||||||
padding: 3px;
|
font-size: 1.2em;
|
||||||
}
|
font-weight: bold;
|
||||||
|
background-color: black;
|
||||||
#search-input-wrapper {
|
color: white;
|
||||||
grid-area: search_field;
|
padding: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-bar {
|
#search-input-wrapper {
|
||||||
grid-area: search;
|
grid-area: search_field;
|
||||||
background-color: #aaa;
|
}
|
||||||
padding-bottom:3px;
|
|
||||||
}
|
#search-bar {
|
||||||
|
grid-area: search;
|
||||||
#navigation {
|
background-color: #aaa;
|
||||||
grid-area: navigation;
|
padding-bottom:3px;
|
||||||
background-color: #aaa;
|
}
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
#navigation {
|
||||||
}
|
grid-area: navigation;
|
||||||
|
background-color: #aaa;
|
||||||
.autocomplete .active {
|
display: flex;
|
||||||
background-color: #AAA;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.autocomplete, #search-input-wrapper .autocomplete.open {
|
.autocomplete .active {
|
||||||
overflow-y: scroll;
|
background-color: #AAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-input {
|
/* scrollbars are nice, but with them an empty autocomplete box is shown
|
||||||
box-sizing: border-box;
|
|
||||||
border: 0;
|
.autocomplete, #search-input-wrapper .autocomplete.open {
|
||||||
}
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
*/
|
||||||
#search-limit-value {
|
|
||||||
width: 4em;
|
#search-input {
|
||||||
}
|
box-sizing: border-box;
|
||||||
|
border: 0;
|
||||||
.input_date {
|
}
|
||||||
max-width: 10em;
|
|
||||||
}
|
|
||||||
|
#search-limit-value {
|
||||||
#add-filter {
|
width: 4em;
|
||||||
float:right;
|
}
|
||||||
}
|
|
||||||
|
.input_date {
|
||||||
#button-bar {
|
max-width: 10em;
|
||||||
text-align: right;
|
}
|
||||||
}
|
|
||||||
|
#add-filter {
|
||||||
#search-submit {
|
float:right;
|
||||||
margin-right:3px;
|
}
|
||||||
}
|
|
||||||
|
#button-bar {
|
||||||
#result-view {
|
text-align: right;
|
||||||
grid-area: result;
|
}
|
||||||
background: #eee;
|
|
||||||
margin: 0;
|
#search-submit {
|
||||||
padding: 0;
|
margin-left:3px;
|
||||||
overflow: hidden;
|
margin-right:3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#result-view i {
|
#result-view {
|
||||||
background: white;
|
grid-area: result;
|
||||||
display:inline;
|
background: #eee;
|
||||||
font-style:normal;
|
margin: 0;
|
||||||
line-height: 1.2em;
|
padding: 0;
|
||||||
}
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.center
|
#result-view i {
|
||||||
{
|
background: white;
|
||||||
display: flex;
|
display:inline;
|
||||||
justify-content: center;
|
font-style:normal;
|
||||||
align-items: center;
|
line-height: 1.2em;
|
||||||
height: 100%;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
input:required:invalid {
|
.center
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAT1JREFUeNpi/P//PwMpgImBRMACY/x7/uDX39sXt/67cMoDyOVgMjBjYFbV/8kkqcCBrIER5KS/967s+rmkXxzI5wJiRSBm/v8P7NTfHHFFl5mVdIzhGv4+u///x+xmuAlcdXPB9KeqeLgYd3bDU2ZpRRmwH4DOeAI07QXIRKipYPD35184/nn17CO4p/+cOfjl76+/X4GYAYThGn7/g+Mfh/ZZwjUA/aABpJVhpv6+dQUjZP78Z0YEK7OezS2gwltg64GmfTu6i+HL+mUMP34wgvGvL78ZOEysf8M1sGgZvQIqfA1SDAL8iUUMPIFRQLf+AmMQ4DQ0vYYSrL9vXDz2sq9LFsiX4dLRA0t8OX0SHKzi5bXf2HUMBVA0gN356N7p7xdOS3w5fAgcfNxWtn+BJi9gVVBOQfYPQIABABvRq3BwGT3OAAAAAElFTkSuQmCC);
|
{
|
||||||
background-position: right top;
|
display: flex;
|
||||||
background-repeat: no-repeat;
|
justify-content: center;
|
||||||
box-shadow: none;
|
align-items: center;
|
||||||
}
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:required:invalid {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAT1JREFUeNpi/P//PwMpgImBRMACY/x7/uDX39sXt/67cMoDyOVgMjBjYFbV/8kkqcCBrIER5KS/967s+rmkXxzI5wJiRSBm/v8P7NTfHHFFl5mVdIzhGv4+u///x+xmuAlcdXPB9KeqeLgYd3bDU2ZpRRmwH4DOeAI07QXIRKipYPD35184/nn17CO4p/+cOfjl76+/X4GYAYThGn7/g+Mfh/ZZwjUA/aABpJVhpv6+dQUjZP78Z0YEK7OezS2gwltg64GmfTu6i+HL+mUMP34wgvGvL78ZOEysf8M1sGgZvQIqfA1SDAL8iUUMPIFRQLf+AmMQ4DQ0vYYSrL9vXDz2sq9LFsiX4dLRA0t8OX0SHKzi5bXf2HUMBVA0gN356N7p7xdOS3w5fAgcfNxWtn+BJi9gVVBOQfYPQIABABvRq3BwGT3OAAAAAElFTkSuQmCC);
|
||||||
|
background-position: right top;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,330 +1,331 @@
|
|||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
||||||
$('#search-submit').click(plot);
|
$('#search-submit').click(plot);
|
||||||
|
|
||||||
renderGroupBy();
|
renderGroupBy();
|
||||||
updateSearchLimitValue();
|
updateSearchLimitValue();
|
||||||
|
|
||||||
$('#search-limit-by').change(updateSearchLimitValue);
|
$('#search-limit-by').change(updateSearchLimitValue);
|
||||||
|
|
||||||
$('#nav_left').click(dateLeftShift);
|
$('#nav_left').click(dateLeftShift);
|
||||||
$('#nav_left_half').click(dateHalfLeftShift);
|
$('#nav_left_half').click(dateHalfLeftShift);
|
||||||
$('#nav_right_half').click(dateHalfRightShift);
|
$('#nav_right_half').click(dateHalfRightShift);
|
||||||
$('#nav_right').click(dateRightShift);
|
$('#nav_right').click(dateRightShift);
|
||||||
|
|
||||||
$('#zoom_in').click(zoomIn);
|
$('#zoom_in').click(zoomIn);
|
||||||
$('#zoom_out').click(zoomOut);
|
$('#zoom_out').click(zoomOut);
|
||||||
|
|
||||||
AutoComplete({
|
AutoComplete({
|
||||||
HttpMethod: "GET",
|
HttpMethod: "GET",
|
||||||
Delay: 300,
|
Delay: 300,
|
||||||
_QueryArg: function() {
|
_QueryArg: function() {
|
||||||
var caretIndex = document.getElementById('search-input').selectionStart + 1;
|
var caretIndex = document.getElementById('search-input').selectionStart + 1;
|
||||||
return 'caretIndex=' + caretIndex + '&query';
|
return 'caretIndex=' + caretIndex + '&query';
|
||||||
},
|
},
|
||||||
_Pre: function() {
|
_Pre: function() {
|
||||||
return encodeURI(this.Input.value);
|
return encodeURI(this.Input.value);
|
||||||
},
|
},
|
||||||
_Post: function(response) {
|
_Post: function(response) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var responseObject = JSON.parse(response);
|
var responseObject = JSON.parse(response);
|
||||||
responseObject['proposals'].forEach(function(item, index){
|
responseObject['proposals'].forEach(function(item, index){
|
||||||
var proposal = {};
|
var proposal = {};
|
||||||
proposal['Label'] = item.value;
|
proposal['Label'] = item.value;
|
||||||
proposal['Value'] = item.proposedQuery;
|
proposal['Value'] = item.proposedQuery;
|
||||||
|
|
||||||
result.push(proposal);
|
result.push(proposal);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(JSON.stringify(result));
|
console.log(JSON.stringify(result));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function zoomIn()
|
function zoomIn()
|
||||||
{
|
{
|
||||||
shiftDate(0.25);
|
shiftDate(0.25);
|
||||||
zoom(0.5);
|
zoom(0.5);
|
||||||
plot();
|
plot();
|
||||||
}
|
}
|
||||||
|
|
||||||
function zoomOut()
|
function zoomOut()
|
||||||
{
|
{
|
||||||
shiftDate(-0.5);
|
shiftDate(-0.5);
|
||||||
zoom(2);
|
zoom(2);
|
||||||
plot();
|
plot();
|
||||||
}
|
}
|
||||||
|
|
||||||
function dateLeftShift()
|
function dateLeftShift()
|
||||||
{
|
{
|
||||||
shiftDate(-1);
|
shiftDate(-1);
|
||||||
plot();
|
plot();
|
||||||
}
|
}
|
||||||
function dateHalfLeftShift()
|
function dateHalfLeftShift()
|
||||||
{
|
{
|
||||||
shiftDate(-0.5);
|
shiftDate(-0.5);
|
||||||
plot();
|
plot();
|
||||||
}
|
}
|
||||||
|
|
||||||
function dateHalfRightShift()
|
function dateHalfRightShift()
|
||||||
{
|
{
|
||||||
shiftDate(0.5);
|
shiftDate(0.5);
|
||||||
plot();
|
plot();
|
||||||
}
|
}
|
||||||
function dateRightShift()
|
function dateRightShift()
|
||||||
{
|
{
|
||||||
shiftDate(1);
|
shiftDate(1);
|
||||||
plot();
|
plot();
|
||||||
}
|
}
|
||||||
|
|
||||||
function zoom(factor)
|
function zoom(factor)
|
||||||
{
|
{
|
||||||
if (!$('#search-date-range').is(":invalid")) {
|
if (!$('#search-date-range').is(":invalid")) {
|
||||||
|
|
||||||
var dateRange = $('#search-date-range').val();
|
var dateRange = $('#search-date-range').val();
|
||||||
var tokens = dateRange.split(/ +/,2);
|
var tokens = dateRange.split(/ +/,2);
|
||||||
|
|
||||||
if(tokens.length == 2)
|
if(tokens.length == 2)
|
||||||
{
|
{
|
||||||
var value = parseInt(tokens[0]);
|
var value = parseInt(tokens[0]);
|
||||||
var period = tokens[1];
|
var period = tokens[1];
|
||||||
|
|
||||||
var newValue = value*factor;
|
var newValue = value*factor;
|
||||||
while (newValue != Math.round(newValue)){
|
while (newValue != Math.round(newValue)){
|
||||||
|
|
||||||
switch (period) {
|
switch (period) {
|
||||||
case "second":
|
case "second":
|
||||||
case "seconds":
|
case "seconds":
|
||||||
if (value == 1) {
|
if (value == 1) {
|
||||||
// we reached the smallest range
|
// we reached the smallest range
|
||||||
}
|
}
|
||||||
else if (value % 2 == 1){
|
else if (value % 2 == 1){
|
||||||
value = value -1;
|
value = value -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "minute":
|
case "minute":
|
||||||
case "minutes":
|
case "minutes":
|
||||||
value = value * 60;
|
value = value * 60;
|
||||||
period = "seconds";
|
period = "seconds";
|
||||||
break;
|
break;
|
||||||
case "hour":
|
case "hour":
|
||||||
case "hours":
|
case "hours":
|
||||||
value = value * 60;
|
value = value * 60;
|
||||||
period = "minutes";
|
period = "minutes";
|
||||||
break;
|
break;
|
||||||
case "day":
|
case "day":
|
||||||
case "days":
|
case "days":
|
||||||
value = value * 24;
|
value = value * 24;
|
||||||
period = "hours";
|
period = "hours";
|
||||||
break;
|
break;
|
||||||
case "week":
|
case "week":
|
||||||
case "weeks":
|
case "weeks":
|
||||||
value = value * 7;
|
value = value * 7;
|
||||||
period = "days";
|
period = "days";
|
||||||
break;
|
break;
|
||||||
case "month":
|
case "month":
|
||||||
case "months":
|
case "months":
|
||||||
value = value * 30;
|
value = value * 30;
|
||||||
period = "days";
|
period = "days";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("unhandled value: "+ period);
|
console.log("unhandled value: "+ period);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
newValue = value*factor
|
newValue = value*factor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$('#search-date-range').val(newValue + " " + period);
|
$('#search-date-range').val(newValue + " " + period);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function shiftDate(directionalFactor)
|
function shiftDate(directionalFactor)
|
||||||
{
|
{
|
||||||
var dateBefore = Date.parse($('#search-date-from').val());
|
var dateBefore = Date.parse($('#search-date-from').val());
|
||||||
var newDate = shiftByInterval(dateBefore, directionalFactor);
|
var newDate = shiftByInterval(dateBefore, directionalFactor);
|
||||||
$('#search-date-from').val(newDate.toString("yyyy-MM-dd HH:mm:ss"));
|
$('#search-date-from').val(newDate.toString("yyyy-MM-dd HH:mm:ss"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function shiftByInterval(date, directionalFactor)
|
function shiftByInterval(date, directionalFactor)
|
||||||
{
|
{
|
||||||
if (!$('#search-date-range').is(":invalid")) {
|
if (!$('#search-date-range').is(":invalid")) {
|
||||||
|
|
||||||
var dateRange = $('#search-date-range').val();
|
var dateRange = $('#search-date-range').val();
|
||||||
var tokens = dateRange.split(/ +/,2);
|
var tokens = dateRange.split(/ +/,2);
|
||||||
|
|
||||||
if(tokens.length == 2)
|
if(tokens.length == 2)
|
||||||
{
|
{
|
||||||
var value = parseInt(tokens[0]);
|
var value = parseInt(tokens[0]);
|
||||||
var period = tokens[1];
|
var period = tokens[1];
|
||||||
var config = {};
|
var config = {};
|
||||||
|
|
||||||
value = directionalFactor * value;
|
value = directionalFactor * value;
|
||||||
|
|
||||||
switch (period) {
|
switch (period) {
|
||||||
case "second":
|
case "second":
|
||||||
case "seconds":
|
case "seconds":
|
||||||
config = { seconds: value };
|
config = { seconds: value };
|
||||||
break;
|
break;
|
||||||
case "minute":
|
case "minute":
|
||||||
case "minutes":
|
case "minutes":
|
||||||
config = { minutes: value };
|
config = { minutes: value };
|
||||||
break;
|
break;
|
||||||
case "hour":
|
case "hour":
|
||||||
case "hours":
|
case "hours":
|
||||||
config = { minutes: 60*value };
|
config = { minutes: 60*value };
|
||||||
break;
|
break;
|
||||||
case "day":
|
case "day":
|
||||||
case "days":
|
case "days":
|
||||||
config = { days: value };
|
config = { days: value };
|
||||||
break;
|
break;
|
||||||
case "week":
|
case "week":
|
||||||
case "weeks":
|
case "weeks":
|
||||||
config = { days: 7*value };
|
config = { days: 7*value };
|
||||||
break;
|
break;
|
||||||
case "month":
|
case "month":
|
||||||
case "months":
|
case "months":
|
||||||
config = { days: 30*value };
|
config = { days: 30*value };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newDate = date.add(config);
|
var newDate = date.add(config);
|
||||||
return newDate;
|
return newDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSearchLimitValue () {
|
function updateSearchLimitValue () {
|
||||||
var optionSelected = $('#search-limit-by').find("option:selected");
|
var optionSelected = $('#search-limit-by').find("option:selected");
|
||||||
var valueSelected = optionSelected.val();
|
var valueSelected = optionSelected.val();
|
||||||
|
|
||||||
if (valueSelected == "NO_LIMIT"){
|
if (valueSelected == "NO_LIMIT"){
|
||||||
$('#search-limit-value').hide();
|
$('#search-limit-value').hide();
|
||||||
}else{
|
}else{
|
||||||
$('#search-limit-value').show();
|
$('#search-limit-value').show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderGroupBy()
|
function renderGroupBy()
|
||||||
{
|
{
|
||||||
var request = {};
|
var request = {};
|
||||||
|
|
||||||
var success = function(response){
|
var success = function(response){
|
||||||
|
|
||||||
initSearchGroupBy('#search-group-by-1', response);
|
initSearchGroupBy('#search-group-by-1', response);
|
||||||
initSearchGroupBy('#search-group-by-2', response);
|
initSearchGroupBy('#search-group-by-2', response);
|
||||||
initSearchGroupBy('#search-group-by-3', response);
|
initSearchGroupBy('#search-group-by-3', response);
|
||||||
};
|
};
|
||||||
var error = function(e) {
|
var error = function(e) {
|
||||||
$('#result-view').text("FAILED: " + JSON.parse(e.responseText).message);
|
$('#result-view').text("FAILED: " + JSON.parse(e.responseText).message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
getJson("fields", request, success, error);
|
getJson("fields", request, success, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSearchGroupBy(selector, response)
|
function initSearchGroupBy(selector, response)
|
||||||
{
|
{
|
||||||
$(selector).empty();
|
$(selector).empty();
|
||||||
var option = new Option("", "");
|
var option = new Option("", "");
|
||||||
$(selector).append($(option));
|
$(selector).append($(option));
|
||||||
|
|
||||||
response.forEach(
|
response.forEach(
|
||||||
(item, index) => {
|
(item, index) => {
|
||||||
var option = new Option(item, item);
|
var option = new Option(item, item);
|
||||||
$(selector).append($(option));
|
$(selector).append($(option));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLoadingIcon()
|
function showLoadingIcon()
|
||||||
{
|
{
|
||||||
$('#result-view').html("<div class='center'><div class='uil-cube-css' style='-webkit-transform:scale(0.41)'><div /><div></div><div></div><div></div></div></div>");
|
$('#result-view').html("<div class='center'><div class='uil-cube-css' style='-webkit-transform:scale(0.41)'><div /><div></div><div></div><div></div></div></div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
function plot(event){
|
function plot(event){
|
||||||
if(event){
|
if(event){
|
||||||
event.preventDefault(); // prevent submit of form which would reload the page
|
event.preventDefault(); // prevent submit of form which would reload the page
|
||||||
}
|
}
|
||||||
|
|
||||||
showLoadingIcon();
|
showLoadingIcon();
|
||||||
var request = {};
|
var request = {};
|
||||||
request['query'] = $('#search-input').val();
|
request['query'] = $('#search-input').val();
|
||||||
request['height'] = $('#result-view').height();
|
request['height'] = $('#result-view').height();
|
||||||
request['width'] = $('#result-view').width();
|
request['width'] = $('#result-view').width();
|
||||||
request['groupBy'] = groupBy();
|
request['groupBy'] = groupBy();
|
||||||
request['limitBy'] = $('#search-limit-by').val();
|
request['limitBy'] = $('#search-limit-by').val();
|
||||||
request['limit'] = parseInt($('#search-limit-value').val());
|
request['limit'] = parseInt($('#search-limit-value').val());
|
||||||
request['dateFrom'] = $('#search-date-from').val();
|
request['dateFrom'] = $('#search-date-from').val();
|
||||||
request['dateRange'] = $('#search-date-range').val();
|
request['dateRange'] = $('#search-date-range').val();
|
||||||
request['axisScale'] = $('#search-y-axis-scale').val();
|
request['axisScale'] = $('#search-y-axis-scale').val();
|
||||||
request['aggregate'] = $('#show-aggregate').val();
|
request['aggregate'] = $('#show-aggregate').val();
|
||||||
|
request['keyOutside'] = $('#key-outside').is(":checked");
|
||||||
|
|
||||||
var success = function(response){
|
|
||||||
|
var success = function(response){
|
||||||
$('#result-view').html('<img src=\"'+response.imageUrls+'" />');
|
|
||||||
};
|
$('#result-view').html('<img src=\"'+response.imageUrls+'" />');
|
||||||
var error = function(e) {
|
};
|
||||||
|
var error = function(e) {
|
||||||
if (e.status == 404){
|
|
||||||
$('#result-view').text("No data points found.");
|
if (e.status == 404){
|
||||||
}
|
$('#result-view').text("No data points found.");
|
||||||
else{
|
}
|
||||||
$('#result-view').text("FAILED: " + JSON.parse(e.responseText).message);
|
else{
|
||||||
}
|
$('#result-view').text("FAILED: " + JSON.parse(e.responseText).message);
|
||||||
};
|
}
|
||||||
|
};
|
||||||
postJson("plots", request, success, error);
|
|
||||||
|
postJson("plots", request, success, error);
|
||||||
}
|
|
||||||
|
}
|
||||||
function groupBy()
|
|
||||||
{
|
function groupBy()
|
||||||
var result = [];
|
{
|
||||||
|
var result = [];
|
||||||
for (var i = 1; i <= 3; i++)
|
|
||||||
{
|
for (var i = 1; i <= 3; i++)
|
||||||
if ($('#search-group-by-'+i).val() != "")
|
{
|
||||||
{
|
if ($('#search-group-by-'+i).val() != "")
|
||||||
result.push($('#search-group-by-'+i).val());
|
{
|
||||||
}
|
result.push($('#search-group-by-'+i).val());
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
function postJson(url, requestData, successCallback, errorCallback) {
|
|
||||||
|
function postJson(url, requestData, successCallback, errorCallback) {
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
$.ajax({
|
||||||
url: url,
|
type: "POST",
|
||||||
data: JSON.stringify(requestData),
|
url: url,
|
||||||
contentType: 'application/json'
|
data: JSON.stringify(requestData),
|
||||||
})
|
contentType: 'application/json'
|
||||||
.done(successCallback)
|
})
|
||||||
.fail(errorCallback);
|
.done(successCallback)
|
||||||
}
|
.fail(errorCallback);
|
||||||
|
}
|
||||||
function getJson(url, requestData, successCallback, errorCallback) {
|
|
||||||
|
function getJson(url, requestData, successCallback, errorCallback) {
|
||||||
$.ajax({
|
|
||||||
type: "GET",
|
$.ajax({
|
||||||
url: url,
|
type: "GET",
|
||||||
data: requestData,
|
url: url,
|
||||||
contentType: 'application/json'
|
data: requestData,
|
||||||
})
|
contentType: 'application/json'
|
||||||
.done(successCallback)
|
})
|
||||||
.fail(errorCallback);
|
.done(successCallback)
|
||||||
}
|
.fail(errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,83 +1,96 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<script type="text/javascript" src="js/jquery-3.2.0.min.js"></script>
|
<script type="text/javascript" src="js/jquery-3.2.0.min.js"></script>
|
||||||
<script type="text/javascript" src="js/search.js"></script>
|
<script type="text/javascript" src="js/search.js"></script>
|
||||||
<script type="text/javascript" src="js/autocomplete.js"></script>
|
<script type="text/javascript" src="js/autocomplete.js"></script>
|
||||||
<script type="text/javascript" src="js/date.js"></script>
|
<script type="text/javascript" src="js/date.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="css/typography.css">
|
<link rel="stylesheet" type="text/css" href="css/typography.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/design.css">
|
<link rel="stylesheet" type="text/css" href="css/design.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/loading.css">
|
<link rel="stylesheet" type="text/css" href="css/loading.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/autocomplete.min.css">
|
<link rel="stylesheet" type="text/css" href="css/autocomplete.min.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/font-awesome.min.css">
|
<link rel="stylesheet" type="text/css" href="css/font-awesome.min.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="logo" aria-hidden="true">LOGO</div>
|
<div id="logo" aria-hidden="true">LOGO</div>
|
||||||
|
|
||||||
<div id="search-input-wrapper">
|
<div id="search-input-wrapper">
|
||||||
<input id="search-input" placeholder="field=value and anotherField=anotherValue" data-autocomplete="autocomplete"
|
<input id="search-input" placeholder="field=value and anotherField=anotherValue" data-autocomplete="autocomplete"
|
||||||
data-autocomplete-empty-message="nothing found" />
|
data-autocomplete-empty-message="nothing found" />
|
||||||
</div>
|
</div>
|
||||||
<div id="search-bar">
|
<div id="search-bar">
|
||||||
<form>
|
<form>
|
||||||
<label for="search-group-by-1">Group By:</label>
|
<div id="search-settings-bar">
|
||||||
<select id="search-group-by-1"></select>
|
<div class="group">
|
||||||
<select id="search-group-by-2"></select>
|
<label for="search-group-by-1">Group By:</label>
|
||||||
<select id="search-group-by-3"></select>
|
<select id="search-group-by-1"></select>
|
||||||
|
<select id="search-group-by-2"></select>
|
||||||
<label for="search-limit-by">Limit By:</label>
|
<select id="search-group-by-3"></select>
|
||||||
<select id="search-limit-by">
|
</div>
|
||||||
<option value="NO_LIMIT" selected="selected">no limit</option>
|
<div class="group">
|
||||||
<option value="MOST_VALUES">most values</option>
|
<label for="search-limit-by">Limit By:</label>
|
||||||
<option value="FEWEST_VALUES">fewest values</option>
|
<select id="search-limit-by">
|
||||||
<option value="MAX_VALUE">max value</option>
|
<option value="NO_LIMIT" selected="selected">no limit</option>
|
||||||
<option value="MIN_VALUE">min value</option>
|
<option value="MOST_VALUES">most values</option>
|
||||||
</select>
|
<option value="FEWEST_VALUES">fewest values</option>
|
||||||
<input type="number" id="search-limit-value" name="search-limit-value" min="1" max="1000" value="10"/>
|
<option value="MAX_VALUE">max value</option>
|
||||||
|
<option value="MIN_VALUE">min value</option>
|
||||||
<label for="search-date-from">From Date:</label>
|
</select>
|
||||||
<input id="search-date-from" class="input_date" type="text" value="{{oldestValue}}" required="required" pattern="\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2]\d|3[0-1]) [0-2]\d:[0-5]\d:[0-5]\d">
|
<input type="number" id="search-limit-value" name="search-limit-value" min="1" max="1000" value="10"/>
|
||||||
|
</div>
|
||||||
<label for="search-date-range">Interval:</label>
|
<div class="group">
|
||||||
<input id="search-date-range" type="text" list="ranges" required="required" value="1 week" pattern="\d+ (second|minute|hour|day|week|month)s?">
|
<label for="search-date-from">From Date:</label>
|
||||||
<datalist id="ranges">
|
<input id="search-date-from" class="input_date" type="text" value="{{oldestValue}}" required="required" pattern="\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2]\d|3[0-1]) [0-2]\d:[0-5]\d:[0-5]\d">
|
||||||
<option value="60 seconds">
|
</div>
|
||||||
<option value="5 minutes">
|
<div class="group">
|
||||||
<option value="1 hour">
|
<label for="search-date-range">Interval:</label>
|
||||||
<option value="1 day">
|
<input id="search-date-range" type="text" list="ranges" required="required" value="1 week" pattern="\d+ (second|minute|hour|day|week|month)s?">
|
||||||
<option value="1 week">
|
<datalist id="ranges">
|
||||||
<option value="1 month">
|
<option value="60 seconds">
|
||||||
</datalist>
|
<option value="5 minutes">
|
||||||
|
<option value="1 hour">
|
||||||
<label for="search-y-axis-scale">Y-Axis:</label>
|
<option value="1 day">
|
||||||
<select id="search-y-axis-scale">
|
<option value="1 week">
|
||||||
<option value="LINEAR" selected="selected">linear</option>
|
<option value="1 month">
|
||||||
<option value="LOG10">log 10</option>
|
</datalist>
|
||||||
<option value="LOG2">log 2</option>
|
</div>
|
||||||
</select>
|
<div class="group">
|
||||||
|
<label for="search-y-axis-scale">Y-Axis:</label>
|
||||||
<label for="show-aggregate">Aggregate:</label>
|
<select id="search-y-axis-scale">
|
||||||
<select id="show-aggregate">
|
<option value="LINEAR" selected="selected">linear</option>
|
||||||
<option value="NONE" selected="selected">-</option>
|
<option value="LOG10">log 10</option>
|
||||||
<option value="MEAN">Mean</option>
|
<option value="LOG2">log 2</option>
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
<button id="search-submit"><i class="fa fa-area-chart" aria-hidden="true"></i> Plot</button>
|
<div class="group">
|
||||||
</form>
|
<label for="show-aggregate">Aggregate:</label>
|
||||||
</div>
|
<select id="show-aggregate">
|
||||||
|
<option value="NONE" selected="selected">-</option>
|
||||||
<div id="navigation">
|
<option value="MEAN">Mean</option>
|
||||||
<button id="nav_left"><i class="fa fa-angle-double-left" aria-hidden="true"></i></button>
|
</select>
|
||||||
<button id="nav_left_half"><i class="fa fa-angle-left" aria-hidden="true"></i></button>
|
</div>
|
||||||
<div>
|
<div class="group">
|
||||||
<button id="zoom_in"><i class="fa fa-plus-circle" aria-hidden="true"></i></button>
|
<input type="checkbox" id="key-outside" />
|
||||||
<button id="zoom_out"><i class="fa fa-minus-circle" aria-hidden="true"></i></button>
|
<label for="key-outside">Legend outside</label>
|
||||||
</div>
|
</div>
|
||||||
<button id="nav_right_half"><i class="fa fa-angle-right" aria-hidden="true"></i></button>
|
<button id="search-submit"><i class="fa fa-area-chart" aria-hidden="true"></i> Plot</button>
|
||||||
<button id="nav_right"><i class="fa fa-angle-double-right" aria-hidden="true"></i></button>
|
</div>
|
||||||
</div>
|
|
||||||
<div id="result-view">
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
|
<div id="navigation">
|
||||||
|
<button id="nav_left"><i class="fa fa-angle-double-left" aria-hidden="true"></i></button>
|
||||||
|
<button id="nav_left_half"><i class="fa fa-angle-left" aria-hidden="true"></i></button>
|
||||||
|
<div>
|
||||||
|
<button id="zoom_in"><i class="fa fa-plus-circle" aria-hidden="true"></i></button>
|
||||||
|
<button id="zoom_out"><i class="fa fa-minus-circle" aria-hidden="true"></i></button>
|
||||||
|
</div>
|
||||||
|
<button id="nav_right_half"><i class="fa fa-angle-right" aria-hidden="true"></i></button>
|
||||||
|
<button id="nav_right"><i class="fa fa-angle-double-right" aria-hidden="true"></i></button>
|
||||||
|
</div>
|
||||||
|
<div id="result-view">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user