Commit 14eaa701 authored by Daniel Smith's avatar Daniel Smith

First pass refactoring to use HTML templating

Moved HTML pages into templates with simple token replacement
Updated some HTML styles
parent a95cfbf0
This diff is collapsed.
......@@ -20,7 +20,7 @@
"any-promise": "^1.3.0",
"array-flatten": "^2.1.2",
"awesomplete": "^1.1.4",
"axios": "^0.18.0",
"axios": "^0.19.2",
"batch": "^0.6.1",
"body-parser": "^1.18.3",
"buffer": "^5.4.3",
......@@ -34,7 +34,7 @@
"del": "^5.1.0",
"depd": "^2.0.0",
"destroy": "^1.0.4",
"diskusage": "^1.0.0",
"diskusage": "^1.1.3",
"ee-first": "^1.1.1",
"elapsed-time": "0.0.1",
"email-validator": "^2.0.4",
......@@ -44,7 +44,7 @@
"etag": "^1.8.1",
"events": "^3.0.0",
"express": "^4.16.4",
"express-fileupload": "^1.1.5",
"express-fileupload": "^1.1.6",
"express-validator": "^5.3.1",
"finalhandler": "^1.1.1",
"follow-redirects": "^1.7.0",
......@@ -57,7 +57,7 @@
"ipaddr.js": "^1.9.0",
"is-buffer": "^2.0.3",
"jsum": "^0.1.4",
"lodash": "^4.17.11",
"lodash": "^4.17.19",
"media-typer": "^1.0.1",
"merge-descriptors": "^1.0.1",
"methods": "^1.1.2",
......@@ -67,9 +67,11 @@
"moment": "^2.24.0",
"moment-duration-format": "^2.2.2",
"ms": "^2.1.1",
"mustache": "^4.0.1",
"mz": "^2.7.0",
"nan": "^2.13.2",
"negotiator": "^0.6.1",
"node-fetch": "^2.6.0",
"node-schedule": "^1.3.2",
"nodemailer": "^6.0.0",
"object-assign": "^4.1.1",
......
This diff is collapsed.
exports.id = "toolbox";
const moment = require("moment");
let momentDurationFormatSetup = require("moment-duration-format");
momentDurationFormatSetup(moment);
let Mustache = require("Mustache");
const fetch = require("node-fetch");
let webPort = 8080;
let domain = "QMLBench.intra.qt.io";
if (process.env.HOST_DOMAIN)
domain = process.env.HOST_DOMAIN;
if (process.env.HTTP_PORT)
webPort = process.env.HTTP_PORT;
exports.resultText = resultText;
function resultText(result) {
if (result < 0)
return `Observed improvement: ${Math.abs(result)}%`;
else if (result > 0)
return `Observed regression: ${result}%`;
else
return "-- No change in score --";
}
exports.jobFormatter = jobFormatter;
function jobFormatter(job, hosts, callback) {
// Dynamically format a job for display on the page
let view = {
testType: job.testType,
jobHash: job.jobHash.slice(0, 10),
jobTitle: job.title,
owner: job.owner,
host: job.test_hosts[0],
timeStarted: moment(job.timestamp).format("ddd DD MMM YYYY - HH:mm"),
status: job.status,
commitString: "",
backendString: "",
statusMsg: job.status_msg,
testString: "",
timeEstimate: "",
result: "",
logButton: "",
cancelButton: "",
spinnerVisible: "hidden"
};
view.commitString = job.commit
? job.commit
: job.firstCommit
? job.firstCommit.slice(0, 10) + ".." + job.secondCommit.slice(0, 10)
: job.good_commit
? job.good_commit.slice(0, 10) + ".." + job.bad_commit.slice(0, 10)
: "";
view.backendString =
job.platform == "win32" && job.openGLBackend
? ` - Open GL Backend: ${job.openGLBackend}`
: "";
if (job.resultJSON && job.status != "failed" && job.status != "cancelled") {
if (job.testType == "singleCommit") {
view.resultString = `Benchmark result: ${job.resultJSON.finalResults[0]}`;
} else if (job.testType == "twoCommit") {
view.resultString = `<b>Results: </b>First commit result (${job.firstCommit.slice(0, 10)}): ${
job.resultJSON.finalResults[0]}, Second commit result (${job.secondCommit.slice(0, 10)}): ${
job.resultJSON.finalResults[1]}
<br>
${resultText(job.resultJSON.finalResults[2])}`;
}
if (job.testType == "bisect") {
const bisectResult =
job.resultJSON.bisectResults[1].length > 1
? `Bisect returned multiple commits:
${job.resultJSON.bisectResults[1].slice(0).join(" ")}`
: `Bisect returned a single commit: ${job.resultJSON.bisectResults[1]}`;
view.resultString = `<b>Results: </b>Good commit result (${job.good_commit.slice(0, 10)}): ${
job.resultJSON.baselineResults[0]},
Bad commit result (${job.bad_commit.slice(0, 10)}): ${job.resultJSON.baselineResults[1]}
<br>
${resultText(job.resultJSON.baselineResults[2])}
<br>
${bisectResult}`;
}
}
if (job.test_name)
view.testString = `Benchmark: ${job.test_name}`;
else if (job.custom_benchmark_file)
view.testString = `Custom benchmark: ${job.custom_benchmark_file.name}`;
if (job.startTime && job.status === "running") {
view.spinnerVisible = "visible";
if (job.expectedDuration - (Date.now() - job.startTime) < 1) {
view.timeEstimate = `Job is about ${moment
.duration(
Math.abs(moment.duration(moment
.duration(job.expectedDuration, "ms")
.subtract(moment.duration(moment(Date.now()).diff(moment(job.startTime)))))),
"ms"
)
.format("h [hrs], m [min]")} overdue`;
} else {
view.timeEstimate = `About ${moment
.duration(moment
.duration(job.expectedDuration, "ms")
.subtract(moment.duration(moment(Date.now()).diff(moment(job.startTime)))))
.format("h [hrs], m [min]")} remaining`;
}
} else if (job.status == "staged") {
view.timeEstimate = `Expected to run for about ${moment
.duration(job.expectedDuration, "ms")
.format("h [hrs], m [min]")}`;
}
if (job.status != "staged") {
view.logButton = `<br><button onclick="window.location.href='${
job.status == "running"
? "http://" +
(hosts[hosts.findIndex((x) => x.hostname == job.test_hosts[0])]
? hosts[hosts.findIndex((x) => x.hostname == job.test_hosts[0])]
: { ip: "127.0.0.1" }
).ip
+ ":" + webPort
: ""
}/logs/${job.test_hosts[0]}/${job.jobHash}.txt'">View Log</button>`;
}
if (job.status != "failed" && job.status != "cancelled") {
view.cancelButton =
`<button onclick="doCancel('${job.jobHash}')" ${
job.status == "cancelling" ? "disabled" : ""}>Cancel</button>`;
}
// Return a formatted HTML string of the job to insert into the page.
fetch(`http://${domain}:${webPort}/templates/job.mustache`)
.then((response) => response.text())
.then((template) => {
var rendered = Mustache.render(template, view);
callback(rendered);
});
}
\ No newline at end of file
<head>
<title>QMLBench Regression Tool</title>
<meta http-equiv="Refresh" content="30">
<link rel="shortcut icon" href="assets/favicon.ico" />
</head>
<style>
body {
font: 13px "Helvetica Neue", "Lucida Grande", "Arial";
background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9));
background-repeat: no-repeat;
color: #555;
-webkit-font-smoothing: antialiased;
}
table {
border: 1px solid black;
}
th {
text-align: center;
border: 1px solid black;
padding-left: 15px;
padding-right: 15px;
}
td {
padding-left: 15px;
padding-right: 15px;
text-align: center;
border: 1px solid black;
}
.failed {
color: darkred;
}
.finished {
color: darkgreen;
}
.running {
color: ForestGreen;
animation: RunningBlinker 3s linear infinite;
}
.cancelling {
color: gold;
animation: CancellingBlinker 2s linear infinite;
}
.cancelled {
color: gray;
}
.staged {
color: royalblue;
}
@keyframes CancellingBlinker {
50% {
color: chocolate;
}
}
@keyframes RunningBlinker {
50% {
color: MediumAquamarine;
}
}
@keyframes lds-spin {
0% {
opacity: 1;
-webkit-transform: scale(1.1, 1.1);
transform: scale(1.1, 1.1);
}
100% {
opacity: 0;
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
}
@-webkit-keyframes lds-spin {
0% {
opacity: 1;
-webkit-transform: scale(1.1, 1.1);
transform: scale(1.1, 1.1);
}
100% {
opacity: 0;
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
}
.lds-spin {
position: relative;
}
.lds-spin div>div {
position: absolute;
width: 40px;
height: 40px;
border-radius: 50%;
background: #5079c4;
-webkit-animation: lds-spin 1s linear infinite;
animation: lds-spin 1s linear infinite;
}
.lds-spin div:nth-child(1)>div {
left: 140px;
top: 80px;
-webkit-animation-delay: -0.875s;
animation-delay: -0.875s;
}
.lds-spin>div:nth-child(1) {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transform-origin: 160px 100px;
transform-origin: 160px 100px;
}
.lds-spin div:nth-child(2)>div {
left: 122.42640685999999px;
top: 122.42640685999999px;
-webkit-animation-delay: -0.75s;
animation-delay: -0.75s;
}
.lds-spin>div:nth-child(2) {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-transform-origin: 142.42640686px 142.42640686px;
transform-origin: 142.42640686px 142.42640686px;
}
.lds-spin div:nth-child(3)>div {
left: 80px;
top: 140px;
-webkit-animation-delay: -0.625s;
animation-delay: -0.625s;
}
.lds-spin>div:nth-child(3) {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-transform-origin: 100px 160px;
transform-origin: 100px 160px;
}
.lds-spin div:nth-child(4)>div {
left: 37.57359314px;
top: 122.42640685999999px;
-webkit-animation-delay: -0.5s;
animation-delay: -0.5s;
}
.lds-spin>div:nth-child(4) {
-webkit-transform: rotate(135deg);
transform: rotate(135deg);
-webkit-transform-origin: 57.57359314px 142.42640686px;
transform-origin: 57.57359314px 142.42640686px;
}
.lds-spin div:nth-child(5)>div {
left: 20px;
top: 80px;
-webkit-animation-delay: -0.375s;
animation-delay: -0.375s;
}
.lds-spin>div:nth-child(5) {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
-webkit-transform-origin: 40px 100px;
transform-origin: 40px 100px;
}
.lds-spin div:nth-child(6)>div {
left: 37.57359314px;
top: 37.57359314px;
-webkit-animation-delay: -0.25s;
animation-delay: -0.25s;
}
.lds-spin>div:nth-child(6) {
-webkit-transform: rotate(225deg);
transform: rotate(225deg);
-webkit-transform-origin: 57.57359314px 57.57359314px;
transform-origin: 57.57359314px 57.57359314px;
}
.lds-spin div:nth-child(7)>div {
left: 80px;
top: 20px;
-webkit-animation-delay: -0.125s;
animation-delay: -0.125s;
}
.lds-spin>div:nth-child(7) {
-webkit-transform: rotate(270deg);
transform: rotate(270deg);
-webkit-transform-origin: 100px 40px;
transform-origin: 100px 40px;
}
.lds-spin div:nth-child(8)>div {
left: 122.42640685999999px;
top: 37.57359314px;
-webkit-animation-delay: 0s;
animation-delay: 0s;
}
.lds-spin>div:nth-child(8) {
-webkit-transform: rotate(315deg);
transform: rotate(315deg);
-webkit-transform-origin: 142.42640686px 57.57359314px;
transform-origin: 142.42640686px 57.57359314px;
}
.lds-spin div:nth-child(9)>div {
left: 140px;
top: 80px;
-webkit-animation-delay: 0.125s;
animation-delay: 0.125s;
}
.lds-spin>div:nth-child(9) {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
-webkit-transform-origin: 160px 100px;
transform-origin: 160px 100px;
}
.lds-spin {
width: 30px !important;
height: 30px !important;
-webkit-transform: translate(-15px, -15px) scale(0.15) translate(15px, 15px);
transform: translate(-15px, -15px) scale(0.15) translate(15px, 15px);
}
</style>
<body onload="checkForCancellingAndQueuedJobs()">
<h3>QMLBench Regression Tool: {{myname}}</h3>
<table align="center">
<tbody>
<tr>
<th>Agent Hostname</th>
<th>Online</th>
<th>Free Disk Space</th>
<th>Uptime</th>
<th>IP address</th>
</tr>
{{&agents}}
</tbody>
</table>
<button onclick="location.href='mailto:{{adminEmail}}';" style="position:absolute;right:10px;top:10px;">report
bug / request feature</button>
<br>
<div align="center">
<button onclick="window.location.href='/scheduleJob'">Schedule new job</button>
<button onclick="window.location.href='/refreshRemoteJobs'" style="display:{{displayStyle}}">Refresh Remote
Jobs</button>
<button onclick="window.location.href='/logs'">Browse log directory</button>
<button onclick="window.location.href='/recentErrors'" style="background-color:{{errorsButtonBgColor}}">Recent
system errors
({{recentErrorsCount}})</button>
</div>
<br>
<div id="jobs">
{{&localJobs}}
{{&queuedJobs}}
{{&remoteJobs}}
{{&remoteQueuedJobs}}
<h3>Completed Jobs</h3>
{{&completedJobs}}
<br>
</div>
</body>
<script>
function doCancel(jobHash) {
window.location.href = '/cancelJob/?jobHash=' + jobHash;
}
function checkForCancellingAndQueuedJobs() { // Refresh the page often if there are cancelling jobs or queued jobs while none are running
//console.log(document.getElementById('jobs').innerHTML.search("is cancelling"));
if (document.getElementById('jobs').innerHTML.search("is cancelling") > -1) {
setTimeout(function () { location.reload(true) }, 5000);
} else if (document.getElementById('jobs').innerHTML.search("is running") < 0 && document.getElementById('jobs').innerHTML.search("is staged") > -1) {
setTimeout(function () { location.reload(true) }, 5000);
}
}
</script>
\ No newline at end of file
<li class="{{status}}" style="position:relative">
<div class="lds-css ng-scope">
<div class="lds-spin" style="visibility:{{spinnerVisible}};width:100%;height:100%;position:absolute;left:-35px;top:30%;"><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div></div>
<b>{{testType}}</b> job <font color="#454545"><b>{{jobHash}}: {{jobTitle}}
<br>
User: {{owner}}</b></font> - Host: {{host}} - Timestamp:
{{timeStarted}} is {{status}}
<br>
Tested commit(s) {{commitString}}{{backendString}}
<br>
Last status message: <font color="#454545">{{statusMsg}}</font>
<br>
{{testString}}
{{timeEstimate}}
{{&result}}
{{&logButton}} {{&cancelButton}}
</li>
<br>
\ No newline at end of file
This diff is collapsed.
<head>
<title>Benchmark System Errors</title>
<link rel="shortcut icon" href="assets/favicon.ico" />
</head>
<style>
body {
font: 13px "Helvetica Neue", "Lucida Grande", "Arial";
background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9));
background-repeat: no-repeat;
color: #555;
-webkit-font-smoothing: antialiased;
}
</style>
<body>
<button onclick="window.location.href='/'">Back</button>
<button onclick="window.location.href='/clearRecentErrors'">Clear all errors</button>
<br>
{{&errorList}}
</body>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment