spoc-bot-vr/.report.tmpl

228 lines
7.4 KiB
Cheetah

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
<script src="https://code.highcharts.com/10/highcharts.js"></script>
<script type="module">
const allMessages = {{ json "Marshal" .messages }};
function fillForm() {
const filterableFields = [
"Asset",
"Channel",
"Event",
"EventName",
"Resolved",
"Thread",
];
const fieldsToOptions = {};
filterableFields.map((field) => {fieldsToOptions[field] = {}});
allMessages.map((message) => {
Object.keys(fieldsToOptions).map((field) => {fieldsToOptions[field][message[field]] = true});
});
Object.keys(fieldsToOptions).map((field) => {fieldsToOptions[field] = Object.keys(fieldsToOptions[field]); fieldsToOptions[field].sort();});
document.getElementById("form").innerHTML = Object.keys(fieldsToOptions).map((field) => {
return `
<label for="${field}">${field}</label>
<select name="${field}" multiple ${fieldsToOptions[field].length > 10 ? "size=10" : `size=${fieldsToOptions[field].length}`}>
${fieldsToOptions[field].map((option) => `
<option selected>${option}</option>
`)}
</select>
`
}).join("\n");
}
window.fillForm = fillForm;
function drawAll() {
const messages = filterMessages(allMessages)
dumpEvents(messages);
drawEventVolume(messages)
drawEventVolumeByHour(messages)
drawEventVolumeByAsset(messages)
}
window.drawAll = drawAll;
function dumpEvents(messages) {
const eventToThreads = {};
for(var m of messages) {
if (!eventToThreads[m.Event])
eventToThreads[m.Event] = [];
eventToThreads[m.Event].push(m.Thread);
}
const threadToMessages = {};
for(var m of messages) {
if (!threadToMessages[m.Thread])
threadToMessages[m.Thread] = [];
threadToMessages[m.Thread].push(m);
}
const eventToMessages = {};
for(var e in eventToThreads) {
if (!eventToMessages[e])
eventToMessages[e] = [];
for (var thread of eventToThreads[e])
eventToMessages[e] = eventToMessages[e].concat(threadToMessages[thread]);
}
for(var e in eventToMessages)
eventToMessages[e].sort((a, b) => a.TS - b.TS);
var events = Object.keys(eventToMessages);
events.sort();
events.reverse();
var keys = ["TS", "Event", "EventName", "Latest"];
document.getElementById("events").innerHTML = `
<tr>
<th>TS</th>
<th>Event</th>
<th>EventName</th>
<th>Latest</th>
</tr>
${events.map((e) => `
<tr>
<td><a href="${eventToMessages[e][0].Source}">${new Date(eventToMessages[e][0].TS * 1000).toDateString()}</a></td>
<td><a href="${eventToMessages[e][0].Source}">${eventToMessages[e][0].Event}</a></td>
<td>${eventToMessages[e][0].EventName}</td>
<td><a href="${eventToMessages[e].at(-1).Source}">${eventToMessages[e].at(-1).Plaintext}</a></td>
</tr>
`).join("")}
`;
}
function filterMessages(messages) {
const selects = document.getElementById("form").getElementsByTagName("select");
const fieldsToOptions = {};
for(var select of selects) {
fieldsToOptions[select.name] = [];
for(var option of select.getElementsByTagName("option"))
if (option.selected)
fieldsToOptions[select.name].push(option.innerHTML);
}
return messages.map((m) => {
for(var k in fieldsToOptions) {
if (fieldsToOptions[k].filter((v) => `${v}` == `${m[k]}`).length == 0) {
return null;
}
}
return m;
}).filter((m) => { return m != null });
}
function drawEventVolume(messages) {
drawEventVolumeWith(
messages,
"eventVolume",
(ts) => new Date(1000 * ts).
toLocaleDateString('en-US', {month: 'numeric', day: 'numeric', weekday: 'short'}),
(m) => m.EventName,
);
}
function drawEventVolumeWith(messages, documentId, kify, nameify) {
const points = [];
messages.forEach((m) => {
points.push({x: m.TS, name: nameify(m)});
});
var xs = points.map((point) => point.x);
if (xs && !isNaN(parseFloat(kify(xs[0])))) {
xs = xs.map(kify);
xs.sort((a, b) => parseFloat(a) - parseFloat(b));
} else {
xs.sort();
xs = xs.map(kify);
}
xs = [...new Set(xs)];
const names = [...new Set(points.map((p) => p.name))];
const nameAndData = names.map((name) => {
return {
name: name,
data: xs.map((x) => points.filter((p) => { return p.name == name && kify(p.x) == x }).length),
}
});
draw(documentId, xs, nameAndData);
}
function drawEventVolumeByHour(messages) {
drawEventVolumeWith(
messages,
"eventVolumeByHour",
(ts) => new Date(1000 * ts).getHours(),
(m) => m.EventName,
);
}
function drawEventVolumeByAsset(messages) {}
function draw(documentId, xs, nameAndData) {
document.getElementById(documentId).innerHTML = "";
Highcharts.chart(documentId, {
chart: { type: 'column' },
title: { text: '' },
xAxis: { categories: xs },
yAxis: { allowDecimals: false, title: { text: '' } },
//legend: { enabled: false },
series: nameAndData,
plotOptions: { column: { stacking: 'normal' } },
});
}
</script>
<style>
rows {
display: flex;
flex-direction: column;
flex-grow: 1;
}
columns {
display: flex;
flex-direction: row;
flex-grow: 1;
}
rows, columns { border: 1px solid red; }
</style>
</head>
<body onload="fillForm(); drawAll();" style="max-width: inherit;">
<h1>Report</h1>
<columns>
<form style="width: 16em; flex-shrink: 0;" onsubmit="drawAll(); return false;">
<columns>
<button type="submit">Apply</button>
</columns>
<rows id="form"></rows>
</form>
<rows>
<rows>
<rows>
<h2>Event Volume</h2>
<div id="eventVolume"></div>
</rows>
<columns>
<rows>
<h3>by Hour</h3>
<div id="eventVolumeByHour"></div>
</rows>
</columns>
<rows>
<h3>by Asset</h3>
<div>DRAW ME</div>
</rows>
</rows>
<rows>
<div>
<h2>Events</h2>
<table id="events">
</table>
</div>
</rows>
</rows>
</columns>
</body>
<footer>
</footer>
</html>