Compare commits

...

4 Commits

Author SHA1 Message Date
02314a3309 refactor: move type definitions into separate file
Also clean up some unnecessary loop labels.
2024-03-11 17:19:09 -04:00
2f6b9b6c58 refactor: extract the scalar plot building 2024-03-10 21:11:32 -04:00
75264dc095 refactor: extract the series plot building 2024-03-10 21:05:49 -04:00
a356eba786 ui: Make the filter boxes a consistent height 2024-03-10 20:45:43 -04:00
2 changed files with 188 additions and 138 deletions

82
static/lib.d.js Normal file
View File

@ -0,0 +1,82 @@
// Copyright 2023 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @typedef PlotList
* @type {object}
* @property {Array=} Series
* @property {Array=} Scalar
* @property {Array=} StreamInstant - Timestamps are in seconds
* @property {Array=} Stream - Timestamps are in nanoseconds
*/
/**
* @typedef QueryData
* @type {object}
* @property {object} yaxes
* @property {?string} legend_orientation
* @property {Array<PlotList>} plots
*/
/**
* @typedef HeaderOrCell
* @type {object}
* @property {array} values
* @property {{color: string}=} fill
* @property {object=} font
* @property {string=} font.family
* @property {number=} font.size
* @property {string=} font.color
* @property {{width: number, color: string}=} line
* @property {Array<number>=} columnwidth
*/
/**
* @typedef TableTrace
* @type {object}
* @property {string=} name
* @property {string} type
* @property {string=} mode
* @property {HeaderOrCell} header
* @property {HeaderOrCell} cells - An Array of columns for the table.
* @property {string=} xaxis
* @property {string=} yaxis
*/
/**
* @typedef GraphTrace
* @type {object}
* @property {string=} name
* @property {string=} fill
* @property type {string}
* @property {string=} mode
* @property {Array} x
* @property {Array} y
* @property {string=} xaxis
* @property {string=} yaxis
*/
/**
* @typedef PlotTrace
* @type {(TableTrace|GraphTrace)}
*/
/**
* @typedef PlotMeta
* @type {object}
* @property {string=} name_format
* @property {string=} yaxis
* @property {("tonexty"|"tozeroy"|"tonextx"|"tozerox"|"toself"|"tonext")=} fill
*/

View File

@ -12,66 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @typedef PlotList
* @type {object}
* @property {Array=} Series
* @property {Array=} Scalar
* @property {Array<{timestamp: string, line: string}>=} StreamInstant - Timestamps are in seconds
* @property {Array<{timestamp: string, line: string}>=} Stream - Timestamps are in nanoseconds
*/
/**
* @typedef QueryData
* @type {object}
* @property {object} yaxes
* @property {?string} legend_orientation
* @property {Array<PlotList>} plots
*/
/**
* @typedef HeaderOrCell
* @type {object}
* @property {array} values
* @property {{color: string}=} fill
* @property {object=} font
* @property {string=} font.family
* @property {number=} font.size
* @property {string=} font.color
* @property {{width: number, color: string}=} line
* @property {Array<number>=} columnwidth
*/
/**
* @typedef TableTrace
* @type {object}
* @property {string=} name
* @property {string} type
* @property {string=} mode
* @property {HeaderOrCell} header
* @property {HeaderOrCell} cells - An Array of columns for the table.
* @property {string=} xaxis
* @property {string=} yaxis
*/
/**
* @typedef GraphTrace
* @type {object}
* @property {string=} name
* @property {string=} fill
* @property type {string}
* @property {string=} mode
* @property {Array} x
* @property {Array} y
* @property {string=} xaxis
* @property {string=} yaxis
*/
/**
* @typedef PlotTrace
* @type {(TableTrace|GraphTrace)}
*/
/**
* Map ansi terminal codes to html color codes.
* @param {string} line
@ -293,7 +233,7 @@ export class GraphPlot extends HTMLElement {
/**
* Formats the name for the plot trace.
* @param {{name_format: ?string}} meta
* @param {PlotMeta} meta
* @param {Map<string, string>} labels
* @return string
*/
@ -337,8 +277,9 @@ export class GraphPlot extends HTMLElement {
const element = document.createElement("div");
const select = document.createElement("select");
select.setAttribute("name", id);
// TODO(jwall): This is how you set boolean attributes. Use the attribute named... :-(
// TODO(jwall): This is how you set boolean attributes. Use the attribute name... :-(
select.setAttribute("multiple", "multiple");
select.setAttribute("size", "3");
const optElement = document.createElement("option");
const optValue = "Select All: " + key;
optElement.innerText = optValue;
@ -440,6 +381,100 @@ export class GraphPlot extends HTMLElement {
};
}
/**
* @param {any} triple
*/
buildSeriesPlot(triple) {
const labels = /** @type {Map<String, String>} */(triple[0]);
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
return null;
}
}
const meta = /** @type {PlotMeta} */(triple[1]);
var yaxis = meta.yaxis || "y";
// https://plotly.com/javascript/reference/layout/yaxis/
const series = triple[2];
const trace = /** @type GraphTrace */({
type: "scatter",
mode: "lines+text",
x: [],
y: [],
// We always share the x axis for timeseries graphs.
xaxis: "x",
yaxis: yaxis,
//yhoverformat: yaxis.tickformat,
});
if (meta.fill) {
trace.fill = meta.fill;
}
var name = this.formatName(meta, labels);
if (name) { trace.name = name; }
for (const point of series) {
trace.x.push(new Date(point.timestamp * 1000));
trace.y.push(point.value);
}
return trace;
}
/**
* @param {any} triple
*/
buildScalarPlot(triple) {
const labels = /** @type {Map<String,String>} */(triple[0]);
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
return null;
}
}
const meta = /** @type {PlotMeta} */(triple[1]);
const series = triple[2];
const trace = /** @type GraphTrace */({
type: "bar",
x: [],
y: [],
yhoverformat: meta["d3_tick_format"],
});
var name = this.formatName(meta, labels);
if (name) { trace.name = name; }
trace.y.push(series.value);
trace.x.push(trace.name);
return trace;
}
/**
* @param {Array} stream
*/
buildStreamPlot(stream) {
const dateColumn = [];
const metaColumn = [];
const logColumn = [];
loopStream: for (const pair of stream) {
const labels = pair[0];
var labelList = [];
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
continue loopStream;
}
labelList.push(`${label}:${labels[label]}`);
}
const labelsName = labelList.join("<br>");
const lines = pair[1];
for (const line of lines) {
// For streams the timestamps are in nanoseconds
let timestamp = new Date(line.timestamp / 1000000);
dateColumn.push(timestamp.toISOString());
metaColumn.push(labelsName);
logColumn.push(ansiToHtml(line.line));
}
}
return [dateColumn, metaColumn, logColumn];
}
/**
* Update the graph with new data.
*
@ -482,62 +517,19 @@ export class GraphPlot extends HTMLElement {
var nextYaxis = this.yaxisNameGenerator();
if (subplot.Series) {
// https://plotly.com/javascript/reference/scatter/
loopSeries: for (const triple of subplot.Series) {
const labels = triple[0];
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
continue loopSeries;
}
for (const triple of subplot.Series) {
const trace = this.buildSeriesPlot(triple);
if (trace) {
traces.push(trace);
}
const meta = triple[1];
var yaxis = meta.yaxis || "y";
// https://plotly.com/javascript/reference/layout/yaxis/
const series = triple[2];
const trace = /** @type GraphTrace */({
type: "scatter",
mode: "lines+text",
x: [],
y: [],
// We always share the x axis for timeseries graphs.
xaxis: "x",
yaxis: yaxis,
//yhoverformat: yaxis.tickformat,
});
if (meta.fill) {
trace.fill = meta.fill;
}
var name = this.formatName(meta, labels);
if (name) { trace.name = name; }
for (const point of series) {
trace.x.push(new Date(point.timestamp * 1000));
trace.y.push(point.value);
}
traces.push(trace);
}
} else if (subplot.Scalar) {
// https://plotly.com/javascript/reference/bar/
loopScalar: for (const triple of subplot.Scalar) {
const labels = triple[0];
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
continue loopScalar;
}
for (const triple of subplot.Scalar) {
const trace = this.buildScalarPlot(triple);
if (trace) {
traces.push(trace);
}
const meta = triple[1];
const series = triple[2];
const trace = /** @type GraphTrace */({
type: "bar",
x: [],
y: [],
yhoverformat: meta["d3_tick_format"],
});
var name = this.formatName(meta, labels);
if (name) { trace.name = name; }
trace.y.push(series.value);
trace.x.push(trace.name);
traces.push(trace);
}
} else if (subplot.Stream) {
// TODO(jwall): It would be nice if scroll behavior would handle replots better.
@ -558,31 +550,7 @@ export class GraphPlot extends HTMLElement {
fill: { color: layout.plot_bgcolor }
},
});
const dateColumn = [];
const metaColumn = [];
const logColumn = [];
loopStream: for (const pair of subplot.Stream) {
const labels = pair[0];
var labelList = [];
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
continue loopStream;
}
labelList.push(`${label}:${labels[label]}`);
}
const labelsName = labelList.join("<br>");
const lines = pair[1];
// TODO(jwall): Headers
for (const line of lines) {
// For streams the timestamps are in nanoseconds
let timestamp = new Date(line.timestamp / 1000000);
dateColumn.push(timestamp.toISOString());
metaColumn.push(labelsName);
logColumn.push(ansiToHtml(line.line));
}
}
const [dateColumn, metaColumn, logColumn] = this.buildStreamPlot(subplot.Stream);
trace.cells.values.push(dateColumn);
trace.cells.values.push(metaColumn);
trace.cells.values.push(logColumn);