Request metrics again via state

This commit is contained in:
Joscha 2023-10-22 17:35:13 +02:00
parent f77ed130e1
commit c9ff8ab228
3 changed files with 106 additions and 31 deletions

View file

@ -1,3 +1,4 @@
import { MetricsResponse } from "./requests.js";
import { el } from "./util.js";
class Folder {
@ -57,12 +58,50 @@ class Folder {
}
}
export function updateMetricsDiv(div: HTMLElement, metrics: string[]) {
let folder = new Folder();
for (let metric of metrics) {
folder.add(metric);
export class Metrics {
#div: HTMLElement;
#dataId: number | null = null;
constructor(div: HTMLElement) {
this.#div = div;
}
div.textContent = ""; // Remove children
div.append(folder.childrenToHtmlElements());
getSelected(): Set<string> {
const selected = new Set<string>();
const checkedInputs = this.#div.querySelectorAll<HTMLInputElement>("input:checked");
for (const input of checkedInputs) {
selected.add(input.name);
}
return selected;
}
requiresUpdate(dataId: number): boolean {
// At the moment, updating the metrics results in all <details> tags
// closing again. To prevent this (as it can be frustrating if you've
// navigated deep into the metrics hierarchy), we never require updates
// after the initial update.
return this.#dataId === null;
// return this.#dataId === null || this.#dataId < dataId;
}
update(response: MetricsResponse) {
const selected = this.getSelected();
const folder = new Folder();
for (const metric of response.metrics) {
folder.add(metric);
}
this.#div.textContent = ""; // Remove children
this.#div.append(folder.childrenToHtmlElements());
const inputs = this.#div.querySelectorAll<HTMLInputElement>("input");
for (const input of inputs) {
input.checked = selected.has(input.name);
}
this.#dataId = response.dataId;
}
}

View file

@ -1,40 +1,67 @@
import { updateMetricsDiv } from "./metrics.js";
import { MetricsResponse, getMetrics } from "./requests.js";
import { Metrics } from "./metrics.js";
import { getMetrics } from "./requests.js";
export class State {
#metricsDiv: HTMLElement;
#latestGraphId: number = -Infinity;
#latestDataId: number = -Infinity;
#updating: boolean = false;
#metrics: MetricsResponse | null = null;
#metrics: Metrics;
constructor(metricsDiv: HTMLElement) {
this.#metricsDiv = metricsDiv;
#requestingNewMetrics: boolean = false;
// commits (with graph id and data id)
// raw measurements (with graph id and data id)
// processed measurements (with graph id and data id)
constructor(metrics: Metrics) {
this.#metrics = metrics;
}
/**
* Look at current state and try to change it so that it represents what the
* user wants.
* Update state and plot and request new data if necessary. Tries to match
* the user's wishes as closely as possible.
*
* This function is idempotent.
*/
async update() {
if (this.#updating) {
return;
update() {
// TODO Invalidate and update data
// TODO Update graph
this.#requestDataWhereNecessary();
}
//////////////////////////////////
// Requesting and updating data //
//////////////////////////////////
#updateDataId(dataId: number) {
if (dataId > this.#latestDataId) {
this.#latestDataId = dataId;
}
}
#updateGraphId(graphId: number) {
if (graphId > this.#latestGraphId) {
this.#latestGraphId = graphId;
}
}
#requestDataWhereNecessary() {
if (this.#metrics.requiresUpdate(this.#latestDataId)) {
this.#requestMetrics();
}
}
async #requestMetrics() {
if (this.#requestingNewMetrics) return;
console.log("Requesting new metrics");
try {
await this.#update_impl();
this.#requestingNewMetrics = true;
const response = await getMetrics();
this.#updateDataId(response.dataId);
this.#metrics.update(response);
this.update();
} finally {
this.#updating = false;
this.#requestingNewMetrics = false;
}
}
async #update_impl() {
this.#update_metrics();
}
async #update_metrics() {
this.#metrics = await getMetrics();
if (this.#metrics === null) { return; }
updateMetricsDiv(this.#metricsDiv, this.#metrics.metrics);
}
}