mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-13 09:52:38 +00:00
215 lines
5.7 KiB
HTML
215 lines
5.7 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;
|
|
-webkit-animation: spin 2s ease-in-out infinite;
|
|
-moz-animation: spin 2s ease-in-out infinite;
|
|
animation: spin 2s 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;
|
|
}
|
|
|
|
@-moz-keyframes spin {
|
|
100% { -moz-transform: rotate(360deg); }
|
|
}
|
|
|
|
@-webkit-keyframes spin {
|
|
100% { -webkit-transform: rotate(360deg); }
|
|
}
|
|
|
|
@keyframes spin {
|
|
100% { transform:rotate(360deg); }
|
|
}
|
|
</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://clickhouse.com/images/logo.svg">
|
|
<h1>Loading (~10 seconds, ~20 MB)</h1>
|
|
</div>
|
|
<canvas id="canvas"></canvas>
|
|
<script type="text/javascript">
|
|
///////////////////////
|
|
// GLOBAL VARIABLES
|
|
///////////////////////
|
|
let start_date = '2020-06-13';
|
|
const canvasNode = document.getElementById('canvas');
|
|
const infoNode = document.getElementById('info');
|
|
const loadingNode = document.getElementById('loading');
|
|
const failMessageNode = document.getElementById('fail-message');
|
|
|
|
///////////////////////
|
|
// QUERIES
|
|
///////////////////////
|
|
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 "default".checks
|
|
|
|
INNER JOIN
|
|
(
|
|
SELECT test_name, toUInt16(rowNumberInAllBlocks()) AS n FROM
|
|
(
|
|
SELECT DISTINCT test_name
|
|
FROM "default".checks
|
|
WHERE match(test_name, '^\\d+_') AND check_name ILIKE '%stateless%' AND check_start_time > now() - INTERVAL 1 DAY
|
|
ORDER BY test_name
|
|
)
|
|
) 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(rowNumberInAllBlocks()) AS n FROM
|
|
(
|
|
SELECT DISTINCT test_name
|
|
FROM "default".checks
|
|
WHERE match(test_name, '^\\d+_') AND check_name ILIKE '%stateless%' AND check_start_time > now() - INTERVAL 1 DAY
|
|
ORDER BY test_name
|
|
) FORMAT JSONCompact`;
|
|
|
|
///////////////////////
|
|
// MAIN
|
|
///////////////////////
|
|
|
|
(async () => {
|
|
try {
|
|
const [render_data, test_names_data] = await Promise.all([
|
|
loadDataByQuery(render_data_query),
|
|
loadDataByQuery(test_names_query),
|
|
]);
|
|
// we good
|
|
renderResponse(render_data);
|
|
saveTestNames(test_names_data);
|
|
} catch (e) {
|
|
alert(e);
|
|
showFailMessage();
|
|
} finally {
|
|
hideLoader();
|
|
}
|
|
})()
|
|
|
|
///////////////////////
|
|
// SPECIAL FUNCTIONS
|
|
///////////////////////
|
|
|
|
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>
|