ClickHouse/utils/tests-visualizer/index.html
2023-04-29 23:48:45 +02:00

190 lines
5.1 KiB
HTML

<html id="html">
<head>
<meta charset="UTF-8">
<style>
body {
color: white;
background: black;
font-family: sans-serif;
}
.hide {
display: none;
}
#loading {
margin-top: 1em;
}
#loading img {
width: 130px;
display: block;
margin: 30px auto;
animation: spin 10s ease-in-out infinite;
}
h1 {
text-align: center;
}
#info {
position: sticky;
top: 1rem;
z-index: 1;
margin: 1rem;
float: right;
font-size: 16pt;
padding: 0.5rem;
border: 1px solid #111;
}
canvas {
position: absolute;
cursor: pointer;
}
@keyframes spin {
50% { transform:scale(150%); }
100% { transform:scale(100%); }
}
</style>
</head>
<body>
<pre id="info" class="hide"></pre>
<h1 id="fail-message" class="hide">Data not load</h1>
<div id="loading">
<img src="https://presentations.clickhouse.com/images/logo.svg">
<h1>Loading (~10 seconds, ~20 MB)</h1>
</div>
<canvas id="canvas"></canvas>
<script type="text/javascript">
let start_date = '2021-12-01';
const canvasNode = document.getElementById('canvas');
const infoNode = document.getElementById('info');
const loadingNode = document.getElementById('loading');
const failMessageNode = document.getElementById('fail-message');
let render_data_query = `
WITH '${start_date}'::Date AS start_date
SELECT groupArray([d, n, fail]) FROM
(
SELECT n, check_start_time::Date - start_date AS d, max(test_status LIKE 'F%' OR test_status LIKE 'E%') AS fail
FROM checks
INNER JOIN
(
SELECT test_name, toUInt16(row_number() OVER (ORDER BY test_name)) AS n FROM
(
SELECT DISTINCT test_name
FROM checks
WHERE match(test_name, '^\\d+_') AND check_name ILIKE '%stateless%' AND check_start_time > now() - INTERVAL 1 DAY
)
) AS nums
USING (test_name)
WHERE
check_name ILIKE '%stateless%'
AND pull_request_number = 0
AND check_start_time >= start_date
GROUP BY d, n
ORDER BY d, n
)
FORMAT TSV`;
let test_names_query = `
SELECT test_name, toUInt16(row_number() OVER (ORDER BY test_name)) AS n FROM
(
SELECT DISTINCT test_name
FROM checks
WHERE match(test_name, '^\\d+_') AND check_name ILIKE '%stateless%' AND check_start_time > now() - INTERVAL 1 DAY
) FORMAT JSONCompact`;
(async () => {
try {
const [render_data, test_names_data] = await Promise.all([
loadDataByQuery(render_data_query),
loadDataByQuery(test_names_query),
]);
renderResponse(render_data);
saveTestNames(test_names_data);
} catch (e) {
alert(e);
showFailMessage();
} finally {
hideLoader();
}
})()
async function loadDataByQuery(query) {
const response = await fetch(
"https://play.clickhouse.com?user=play&add_http_cors_header=1",
{ method: "POST", body: query }
)
if (!response.ok) throw new Error(`Data download failed\nHTTP status ${response.status}`);
const json = await response.json();
return json;
}
function renderResponse(data) {
const last_pixel = data[data.length - 1];
canvasNode.width = last_pixel[0] + 1;
canvasNode.height = last_pixel[1] + 1;
document.getElementById('html').style.height = canvasNode.height + 10 + 'px';
document.body.style.height = canvasNode.height + 10 + 'px';
let ctx = canvasNode.getContext('2d');
let image = ctx.createImageData(canvasNode.width, canvasNode.height);
let {data: pixels} = image;
data.map(elem => {
let x = elem[0];
let y = canvasNode.height - elem[1];
pixels[(x + y * canvasNode.width) * 4 + 0] = elem[2] ? 255 : 0; // r
pixels[(x + y * canvasNode.width) * 4 + 1] = elem[2] ? 0 : 100; // g
pixels[(x + y * canvasNode.width) * 4 + 2] = 0; // b
pixels[(x + y * canvasNode.width) * 4 + 3] = 255; // a
});
ctx.putImageData(image, 0, 0);
}
function saveTestNames(data) {
let {data: test_names} = data;
canvasNode.addEventListener('mousemove', (event) => {
infoNode.style.display = 'block';
const x = event.layerX;
const y = event.layerY;
let date = new Date(start_date);
date.setDate(date.getDate() + x);
[date] = date.toISOString().split('T');
let [test] = test_names[canvasNode.height - y];
let {data: pixel} = canvasNode.getContext('2d').getImageData(x, y, 1, 1);
updateInfo(date, test, pixel);
})
}
function updateInfo(date, test, pixel) {
infoNode.innerText = `${date}, ${test}`;
infoNode.style.background = pixel[0] > 0 ? '#F00' : (pixel[3] > 0 ? '#006400' : '#000');
}
function hideLoader() {
loadingNode.style.display = 'none';
}
function showFailMessage() {
failMessageNode.style.display = 'block';
}
</script>
</body>
</html>