|
@@ -77,6 +77,7 @@
|
|
|
<option value="3">🥉 bronze</option>
|
|
<option value="3">🥉 bronze</option>
|
|
|
</select>
|
|
</select>
|
|
|
</label>
|
|
</label>
|
|
|
|
|
+ <label><input type="checkbox" id="uponly"> ● running</label>
|
|
|
<details id="cols"><summary>Columns ▾</summary><div id="colbox"></div></details>
|
|
<details id="cols"><summary>Columns ▾</summary><div id="colbox"></div></details>
|
|
|
<label>Rows/page:
|
|
<label>Rows/page:
|
|
|
<select id="size"><option>25</option><option selected>50</option><option>100</option><option value="0">All</option></select>
|
|
<select id="size"><option>25</option><option selected>50</option><option>100</option><option value="0">All</option></select>
|
|
@@ -94,15 +95,26 @@
|
|
|
"use strict";
|
|
"use strict";
|
|
|
let DATA = {{DATA}};
|
|
let DATA = {{DATA}};
|
|
|
const COLS = [
|
|
const COLS = [
|
|
|
- ["name", "Recipe"], ["title", "Title"], ["model", "Model"], ["quant", "Quant"],
|
|
|
|
|
|
|
+ ["name", "Recipe"], ["state", "State"], ["uptime", "Uptime"], ["runs", "Launches"],
|
|
|
|
|
+ ["title", "Title"], ["model", "Model"], ["quant", "Quant"],
|
|
|
["engine", "Engine"], ["image", "Image"], ["nodes", "Nodes"], ["tp", "TP"],
|
|
["engine", "Engine"], ["image", "Image"], ["nodes", "Nodes"], ["tp", "TP"],
|
|
|
["deploy", "Deploy"], ["port", "Port"], ["n", "Reports"], ["last", "Last bench"],
|
|
["deploy", "Deploy"], ["port", "Port"], ["n", "Reports"], ["last", "Last bench"],
|
|
|
["points", "Points"], ["pct", "Score /100"], ["stars", "Bench"],
|
|
["points", "Points"], ["pct", "Score /100"], ["stars", "Bench"],
|
|
|
["tps1", "Tok/s c1"], ["tps2", "Tok/s c2"], ["parent", "Parent"],
|
|
["tps1", "Tok/s c1"], ["tps2", "Tok/s c2"], ["parent", "Parent"],
|
|
|
];
|
|
];
|
|
|
-const NUM = new Set(["nodes", "tp", "port", "n", "pct", "tps1", "tps2", "stars"]);
|
|
|
|
|
|
|
+const NUM = new Set(["nodes", "tp", "port", "n", "pct", "tps1", "tps2", "stars", "state", "uptime", "runs"]);
|
|
|
const MEDAL = { 1: "🥇 ", 2: "🥈 ", 3: "🥉 " };
|
|
const MEDAL = { 1: "🥇 ", 2: "🥈 ", 3: "🥉 " };
|
|
|
|
|
|
|
|
|
|
+// run-ledger helpers: who is up right now, for how long (live — a 30 s tick
|
|
|
|
|
+// re-renders while anything is running)
|
|
|
|
|
+const nowSecs = () => Math.floor(Date.now() / 1000);
|
|
|
|
|
+const fmtUp = s => {
|
|
|
|
|
+ s = Math.max(0, s | 0);
|
|
|
|
|
+ const d = (s / 86400) | 0, h = ((s % 86400) / 3600) | 0, m = ((s % 3600) / 60) | 0;
|
|
|
|
|
+ return d ? d + "d " + h + "h" : h ? h + "h " + m + "m" : m ? m + "m" : s + "s";
|
|
|
|
|
+};
|
|
|
|
|
+const fmtSince = u => new Date(u * 1000).toISOString().slice(0, 16).replace("T", " ");
|
|
|
|
|
+
|
|
|
// star grade of the latest completed bench. The 10-color palette covers
|
|
// star grade of the latest completed bench. The 10-color palette covers
|
|
|
// 0..100 a decade per color (color = which decade the score is in); the
|
|
// 0..100 a decade per color (color = which decade the score is in); the
|
|
|
// star count shows the position WITHIN that decade (1 star = 2 points of
|
|
// star count shows the position WITHIN that decade (1 star = 2 points of
|
|
@@ -142,6 +154,8 @@ function cell(r, k) {
|
|
|
if (k === "pct") { const e = latest(r, e => e.pct != null); return e ? e.pct : ""; }
|
|
if (k === "pct") { const e = latest(r, e => e.pct != null); return e ? e.pct : ""; }
|
|
|
if (k === "tps1" || k === "tps2") { const e = latest(r, e => e.perf && e.perf.length); return tg(e, 0, k === "tps1" ? 1 : 2) ?? ""; }
|
|
if (k === "tps1" || k === "tps2") { const e = latest(r, e => e.perf && e.perf.length); return tg(e, 0, k === "tps1" ? 1 : 2) ?? ""; }
|
|
|
if (k === "stars") { const si = starInfo(r); return si ? si.pct : -1; } // sort by the absolute score
|
|
if (k === "stars") { const si = starInfo(r); return si ? si.pct : -1; } // sort by the absolute score
|
|
|
|
|
+ if (k === "state" || k === "uptime") return r.up != null ? nowSecs() - r.up : -1; // live uptime secs
|
|
|
|
|
+ if (k === "runs") return r.runs ?? 0;
|
|
|
return r[k];
|
|
return r[k];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -286,6 +300,7 @@ const S = {
|
|
|
closed: new Set(String(pick("closed", "")).split("|").map(decodeURIComponent).filter(Boolean)),
|
|
closed: new Set(String(pick("closed", "")).split("|").map(decodeURIComponent).filter(Boolean)),
|
|
|
openRow: pick("open", null),
|
|
openRow: pick("open", null),
|
|
|
medal: pick("medal", ""),
|
|
medal: pick("medal", ""),
|
|
|
|
|
+ uponly: pick("uponly", "") === "1",
|
|
|
tp: TPVALS.includes(+(pick("tp", ""))) ? String(pick("tp", "")) : "",
|
|
tp: TPVALS.includes(+(pick("tp", ""))) ? String(pick("tp", "")) : "",
|
|
|
};
|
|
};
|
|
|
document.getElementById("q").value = S.q;
|
|
document.getElementById("q").value = S.q;
|
|
@@ -293,6 +308,7 @@ document.getElementById("group").value = S.group;
|
|
|
document.getElementById("size").value = String(S.size);
|
|
document.getElementById("size").value = String(S.size);
|
|
|
document.getElementById("tp").value = S.tp;
|
|
document.getElementById("tp").value = S.tp;
|
|
|
document.getElementById("medal").value = S.medal;
|
|
document.getElementById("medal").value = S.medal;
|
|
|
|
|
+document.getElementById("uponly").checked = S.uponly;
|
|
|
function syncUrl() {
|
|
function syncUrl() {
|
|
|
const p = new URLSearchParams();
|
|
const p = new URLSearchParams();
|
|
|
if (S.q) p.set("q", S.q);
|
|
if (S.q) p.set("q", S.q);
|
|
@@ -307,6 +323,7 @@ function syncUrl() {
|
|
|
if (closed.length) p.set("closed", closed.join("|"));
|
|
if (closed.length) p.set("closed", closed.join("|"));
|
|
|
if (S.openRow) p.set("open", S.openRow);
|
|
if (S.openRow) p.set("open", S.openRow);
|
|
|
if (S.medal) p.set("medal", S.medal);
|
|
if (S.medal) p.set("medal", S.medal);
|
|
|
|
|
+ if (S.uponly) p.set("uponly", "1");
|
|
|
if (S.tp) p.set("tp", S.tp);
|
|
if (S.tp) p.set("tp", S.tp);
|
|
|
const qs = p.toString();
|
|
const qs = p.toString();
|
|
|
history.replaceState(null, "", qs ? "?" + qs : location.pathname);
|
|
history.replaceState(null, "", qs ? "?" + qs : location.pathname);
|
|
@@ -314,7 +331,7 @@ function syncUrl() {
|
|
|
localStorage.setItem("report-view", JSON.stringify({
|
|
localStorage.setItem("report-view", JSON.stringify({
|
|
|
q: S.q, group: S.group, sort: S.sort, dir: S.dir, size: S.size,
|
|
q: S.q, group: S.group, sort: S.sort, dir: S.dir, size: S.size,
|
|
|
hidden: [...S.hidden], closed: [...S.closed], openRow: S.openRow,
|
|
hidden: [...S.hidden], closed: [...S.closed], openRow: S.openRow,
|
|
|
- medal: S.medal, tp: S.tp,
|
|
|
|
|
|
|
+ medal: S.medal, tp: S.tp, uponly: S.uponly ? "1" : "",
|
|
|
}));
|
|
}));
|
|
|
} catch { /* private mode etc. — URL still carries the state */ }
|
|
} catch { /* private mode etc. — URL still carries the state */ }
|
|
|
}
|
|
}
|
|
@@ -324,6 +341,7 @@ const esc = s => String(s).replace(/[&<>"]/g, c => ({"&":"&","<":"<",">":
|
|
|
function filtered() {
|
|
function filtered() {
|
|
|
const q = S.q.toLowerCase();
|
|
const q = S.q.toLowerCase();
|
|
|
return DATA.filter(r => (!S.tp || String(r.tp) === S.tp) &&
|
|
return DATA.filter(r => (!S.tp || String(r.tp) === S.tp) &&
|
|
|
|
|
+ (!S.uponly || r.up != null) &&
|
|
|
(!S.medal || (S.medal === "m" ? r.medal != null : String(r.medal) === S.medal)) &&
|
|
(!S.medal || (S.medal === "m" ? r.medal != null : String(r.medal) === S.medal)) &&
|
|
|
(!q || (r.name + " " + r.title + " " + r.model + " " + r.parent + " " + r.quant + " " +
|
|
(!q || (r.name + " " + r.title + " " + r.model + " " + r.parent + " " + r.quant + " " +
|
|
|
r.engine + " " + r.image + " " + r.deploy + " " +
|
|
r.engine + " " + r.image + " " + r.deploy + " " +
|
|
@@ -406,6 +424,14 @@ function render() {
|
|
|
? '<span class="dim" title="bench started, not finished">◐</span>'
|
|
? '<span class="dim" title="bench started, not finished">◐</span>'
|
|
|
: '<span class="dim" title="no bench reports">—</span>'
|
|
: '<span class="dim" title="no bench reports">—</span>'
|
|
|
: `<span title="${si.pct}/100${pe && pe.points ? " (" + pe.points + ")" : ""}">${starGlyphs(si)}</span>`;
|
|
: `<span title="${si.pct}/100${pe && pe.points ? " (" + pe.points + ")" : ""}">${starGlyphs(si)}</span>`;
|
|
|
|
|
+ } else if (c[0] === "state") {
|
|
|
|
|
+ body = r.up != null
|
|
|
|
|
+ ? `<span title="up since ${fmtSince(r.up)} UTC"><span style="color:green">●</span><span class="dim"> up</span></span>`
|
|
|
|
|
+ : '<span class="dim" title="not running">○</span>';
|
|
|
|
|
+ } else if (c[0] === "uptime") {
|
|
|
|
|
+ body = r.up != null ? fmtUp(v) : '<span class="dim">—</span>';
|
|
|
|
|
+ } else if (c[0] === "runs") {
|
|
|
|
|
+ body = v > 0 ? esc(v) : '<span class="dim">0</span>';
|
|
|
} else if (v === "" || v == null) {
|
|
} else if (v === "" || v == null) {
|
|
|
body = '<span class="dim">—</span>';
|
|
body = '<span class="dim">—</span>';
|
|
|
} else if (c[0] === "name") {
|
|
} else if (c[0] === "name") {
|
|
@@ -475,6 +501,10 @@ document.getElementById("q").addEventListener("input", e => { S.q = e.target.val
|
|
|
document.getElementById("group").addEventListener("change", e => { S.group = e.target.value; S.closed.clear(); S.page = 0; render(); });
|
|
document.getElementById("group").addEventListener("change", e => { S.group = e.target.value; S.closed.clear(); S.page = 0; render(); });
|
|
|
document.getElementById("tp").addEventListener("change", e => { S.tp = e.target.value; S.page = 0; render(); });
|
|
document.getElementById("tp").addEventListener("change", e => { S.tp = e.target.value; S.page = 0; render(); });
|
|
|
document.getElementById("medal").addEventListener("change", e => { S.medal = e.target.value; S.page = 0; render(); });
|
|
document.getElementById("medal").addEventListener("change", e => { S.medal = e.target.value; S.page = 0; render(); });
|
|
|
|
|
+document.getElementById("uponly").addEventListener("change", e => { S.uponly = e.target.checked; S.page = 0; render(); });
|
|
|
|
|
+// live uptime: re-render while anything is running (the cell values are
|
|
|
|
|
+// computed at render time)
|
|
|
|
|
+setInterval(() => { if (DATA.some(r => r.up != null)) render(); }, 30000);
|
|
|
document.getElementById("size").addEventListener("change", () => { S.page = 0; render(); });
|
|
document.getElementById("size").addEventListener("change", () => { S.page = 0; render(); });
|
|
|
document.getElementById("prev").addEventListener("click", () => { if (S.page > 0) { S.page--; render(); } });
|
|
document.getElementById("prev").addEventListener("click", () => { if (S.page > 0) { S.page--; render(); } });
|
|
|
document.getElementById("next").addEventListener("click", () => { S.page++; render(); });
|
|
document.getElementById("next").addEventListener("click", () => { S.page++; render(); });
|