mirror of
https://github.com/zaphar/Heracles.git
synced 2025-07-26 22:09:50 -04:00
Compare commits
No commits in common. "a010112f39f81cf2628a40e3b32d949db21eeece" and "0abd67a729ae4d9baf5ffd2a190c8f330a15caa6" have entirely different histories.
a010112f39
...
0abd67a729
155
static/lib.js
155
static/lib.js
@ -23,16 +23,11 @@ class TimeseriesGraph extends HTMLElement {
|
|||||||
#step_duration;
|
#step_duration;
|
||||||
#d3TickFormat = "~s";
|
#d3TickFormat = "~s";
|
||||||
#targetNode = null;
|
#targetNode = null;
|
||||||
#menuContainer = null;
|
|
||||||
#filterSelectElements = {};
|
|
||||||
#filterLabels = {};
|
|
||||||
#filteredLabelSets = {};
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.#width = 800;
|
this.#width = 800;
|
||||||
this.#height = 600;
|
this.#height = 600;
|
||||||
this.#pollSeconds = 30;
|
this.#pollSeconds = 30;
|
||||||
this.#menuContainer = this.appendChild(document.createElement('div'));
|
|
||||||
this.#targetNode = this.appendChild(document.createElement("div"));
|
this.#targetNode = this.appendChild(document.createElement("div"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +62,7 @@ class TimeseriesGraph extends HTMLElement {
|
|||||||
default: // do nothing;
|
default: // do nothing;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.reset();
|
this.resetInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
@ -79,10 +74,9 @@ class TimeseriesGraph extends HTMLElement {
|
|||||||
this.#duration = this.getAttribute('duration') || null;
|
this.#duration = this.getAttribute('duration') || null;
|
||||||
this.#step_duration = this.getAttribute('step-duration') || null;
|
this.#step_duration = this.getAttribute('step-duration') || null;
|
||||||
this.#d3TickFormat = this.getAttribute('d3-tick-format') || this.#d3TickFormat;
|
this.#d3TickFormat = this.getAttribute('d3-tick-format') || this.#d3TickFormat;
|
||||||
var self = this;
|
this.resetInterval()
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
this.stopInterval()
|
this.stopInterval()
|
||||||
}
|
}
|
||||||
@ -100,18 +94,12 @@ class TimeseriesGraph extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(updateOnly) {
|
resetInterval() {
|
||||||
var self = this;
|
this.stopInterval()
|
||||||
self.stopInterval()
|
if (this.#uri) {
|
||||||
self.fetchData().then((data) => {
|
this.updateGraph();
|
||||||
if (!updateOnly) {
|
}
|
||||||
self.getLabelsForData(data);
|
this.#intervalId = setInterval(() => this.updateGraph(), 1000 * this.#pollSeconds);
|
||||||
self.buildFilterMenu();
|
|
||||||
}
|
|
||||||
self.updateGraph(data).then(() => {
|
|
||||||
self.#intervalId = setInterval(() => self.updateGraph(), 1000 * self.#pollSeconds);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static registerElement() {
|
static registerElement() {
|
||||||
@ -129,102 +117,13 @@ class TimeseriesGraph extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
// TODO(zaphar): Can we do some massaging on these
|
|
||||||
// to get the full set of labels and possible values?
|
|
||||||
const response = await fetch(this.getUri());
|
const response = await fetch(this.getUri());
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatName(meta, labels) {
|
async updateGraph() {
|
||||||
var name = "";
|
const data = await this.fetchData();
|
||||||
const formatter = meta.name_format
|
|
||||||
if (formatter) {
|
|
||||||
name = eval(formatter);
|
|
||||||
} else {
|
|
||||||
var names = [];
|
|
||||||
for (const value of labels) {
|
|
||||||
names.push(value);
|
|
||||||
}
|
|
||||||
name = names.join(" ");
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
populateFilterData(labels) {
|
|
||||||
for (var key in labels) {
|
|
||||||
const label = this.#filterLabels[key];
|
|
||||||
if (label) {
|
|
||||||
if (!label.includes(labels[key])) {
|
|
||||||
this.#filterLabels[key].push(labels[key]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.#filterLabels[key] = [labels[key]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildSelectElement(key) {
|
|
||||||
var id = key + "-select" + Math.random();
|
|
||||||
const element = document.createElement("div");
|
|
||||||
const label = document.createElement("label");
|
|
||||||
label.innerText = key + ": ";
|
|
||||||
label.setAttribute("for", id);
|
|
||||||
element.appendChild(label);
|
|
||||||
const select = document.createElement("select");
|
|
||||||
select.setAttribute("name", id);
|
|
||||||
select.setAttribute("multiple", true);
|
|
||||||
const optElement = document.createElement("option");
|
|
||||||
const optValue = "Select " + key;
|
|
||||||
optElement.innerText = optValue;
|
|
||||||
select.appendChild(optElement);
|
|
||||||
for (var opt of this.#filterLabels[key]) {
|
|
||||||
const optElement = document.createElement("option");
|
|
||||||
optElement.setAttribute("value", opt);
|
|
||||||
optElement.innerText = opt;
|
|
||||||
select.appendChild(optElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
select.onchange = function(evt) {
|
|
||||||
evt.stopPropagation();
|
|
||||||
var filteredValues = [];
|
|
||||||
for (var opt of evt.target.selectedOptions) {
|
|
||||||
filteredValues.push(opt.getAttribute("value"));
|
|
||||||
}
|
|
||||||
self.#filteredLabelSets[key] = filteredValues;
|
|
||||||
self.reset(true);
|
|
||||||
};
|
|
||||||
element.appendChild(select);
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFilterMenu() {
|
|
||||||
// We need to maintain a stable order for these
|
|
||||||
var children = [];
|
|
||||||
for (var key of Object.keys(this.#filterLabels).sort()) {
|
|
||||||
const element = this.#filterSelectElements[key] || this.buildSelectElement(key);
|
|
||||||
children.push(element);
|
|
||||||
}
|
|
||||||
this.#menuContainer.replaceChildren(...children);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLabelsForData(data) {
|
|
||||||
for (var subplot of data) {
|
|
||||||
if (subplot.Series) {
|
|
||||||
for (const triple of subplot.Series) {
|
|
||||||
const labels = triple[0];
|
|
||||||
this.populateFilterData(labels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateGraph(maybeData) {
|
|
||||||
var data = maybeData;
|
|
||||||
if (!data) {
|
|
||||||
data = await this.fetchData();
|
|
||||||
}
|
|
||||||
const config = {
|
const config = {
|
||||||
legend: {
|
legend: {
|
||||||
orientation: 'h'
|
orientation: 'h'
|
||||||
@ -241,14 +140,8 @@ class TimeseriesGraph extends HTMLElement {
|
|||||||
const default_yaxis = "y" + subplotCount
|
const default_yaxis = "y" + subplotCount
|
||||||
if (subplot.Series) {
|
if (subplot.Series) {
|
||||||
// https://plotly.com/javascript/reference/scatter/
|
// https://plotly.com/javascript/reference/scatter/
|
||||||
loopSeries: for (const triple of subplot.Series) {
|
for (const triple of subplot.Series) {
|
||||||
const labels = triple[0];
|
const labels = triple[0];
|
||||||
for (var label in labels) {
|
|
||||||
var show = this.#filteredLabelSets[label];
|
|
||||||
if (show && !show.includes(labels[label])) {
|
|
||||||
continue loopSeries;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const meta = triple[1];
|
const meta = triple[1];
|
||||||
const yaxis = meta["named_axis"] || default_yaxis;
|
const yaxis = meta["named_axis"] || default_yaxis;
|
||||||
// https://plotly.com/javascript/reference/layout/yaxis/
|
// https://plotly.com/javascript/reference/layout/yaxis/
|
||||||
@ -267,7 +160,17 @@ class TimeseriesGraph extends HTMLElement {
|
|||||||
yaxis: yaxis,
|
yaxis: yaxis,
|
||||||
yhoverformat: meta["d3_tick_format"],
|
yhoverformat: meta["d3_tick_format"],
|
||||||
};
|
};
|
||||||
var name = this.formatName(meta, labels);
|
var name = "";
|
||||||
|
const formatter = meta.name_format
|
||||||
|
if (formatter) {
|
||||||
|
name = eval(formatter);
|
||||||
|
} else {
|
||||||
|
var names = [];
|
||||||
|
for (const value of labels) {
|
||||||
|
names.push(value);
|
||||||
|
}
|
||||||
|
name = names.join(" ");
|
||||||
|
}
|
||||||
if (name) { trace.name = name; }
|
if (name) { trace.name = name; }
|
||||||
for (const point of series) {
|
for (const point of series) {
|
||||||
trace.x.push(new Date(point.timestamp * 1000));
|
trace.x.push(new Date(point.timestamp * 1000));
|
||||||
@ -287,7 +190,17 @@ class TimeseriesGraph extends HTMLElement {
|
|||||||
y: [],
|
y: [],
|
||||||
yhoverformat: meta["d3_tick_format"],
|
yhoverformat: meta["d3_tick_format"],
|
||||||
};
|
};
|
||||||
var name = this.formatName(meta, labels);
|
const formatter = meta.name_format;
|
||||||
|
var name = "";
|
||||||
|
if (formatter) {
|
||||||
|
name = eval(formatter);
|
||||||
|
} else {
|
||||||
|
var names = [];
|
||||||
|
for (const value of labels) {
|
||||||
|
names.push(value);
|
||||||
|
}
|
||||||
|
name = names.join(" ");
|
||||||
|
}
|
||||||
if (name) { trace.name = name; }
|
if (name) { trace.name = name; }
|
||||||
trace.y.push(series.value);
|
trace.y.push(series.value);
|
||||||
trace.x.push(trace.name);
|
trace.x.push(trace.name);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user