Add dashboard display.

* src/cuirass/http.scm (url-handler): New "/eval/id/dashboard" route.
* src/cuirass/templates.scm (evaluation-dashboard): New procedure.
* src/static/css/cuirass.css (content-fixed-margin, dashboard, div.tooltip):
New sections.
This commit is contained in:
Mathieu Othacehe 2021-04-07 11:47:48 +02:00
parent 63ce1c940b
commit ec0be96407
No known key found for this signature in database
GPG Key ID: 8354763531769CA6
3 changed files with 184 additions and 4 deletions

View File

@ -812,6 +812,25 @@ into a specification record and return it."
(respond-compressed-file log)
(respond-not-found (uri->string (request-uri request))))))
(('GET "eval" (= string->number id) "dashboard")
(let* ((params (request-parameters request))
(system (assq-ref params 'system))
(default-system "x86_64-linux")
(spec-name (db-get-evaluation-specification id))
(spec (db-get-specification spec-name))
(systems (specification-systems spec)))
(respond-html
(html-page
"Dashboard"
(evaluation-dashboard id systems
#:current-system
(or system default-system))
`(((#:name . ,spec-name)
(#:link . ,(string-append "/jobset/" spec-name)))
((#:name . ,(string-append "Evaluation " (number->string id)))
(#:link . ,(string-append "/eval/" (number->string id)))))
#:margin? #f))))
(('GET "search")
(let* ((params (request-parameters request))
(query (and=> (assq-ref params 'query) uri-decode))

View File

@ -52,7 +52,8 @@
running-builds-table
global-metrics-content
workers-status
machine-status))
machine-status
evaluation-dashboard))
(define (navigation-items navigation)
(match navigation
@ -104,7 +105,9 @@ the " (code "guix-master") " specification for the " (code "i686-linux") "
system whose names start with " (code "guile-") ":" (br)
(code "spec:guix-master system:i686-linux status:success guile-")))))
(define* (html-page title body navigation #:optional query)
(define* (html-page title body navigation
#:optional query
#:key (margin? #t))
"Return HTML page with given TITLE and BODY."
`(html (@ (xmlns "http://www.w3.org/1999/xhtml")
(xml:lang "en")
@ -198,7 +201,10 @@ columnDefs: [
Home))
,@(navigation-items navigation)))
,(search-form query))
(div (@ (class "container content"))
(div (@ (id "content")
(class ,(if margin?
"container content"
"content-fixed-margin")))
,body)
(footer
(@ (class "footer text-center"))
@ -844,8 +850,15 @@ if ($('.param-select-row').is(':visible')) {
(td ,(input-changes (assq-ref row #:checkouts)))
(td ,@(evaluation-badges row))
(td
(a (@ (href "/eval/" ,(assq-ref row #:id)
"/dashboard"))
(div
(@ (class "oi oi-monitor d-inline-block")
(title "Dashboard")
(aria-hidden "true"))
""))
(div
(@ (class "dropdown"))
(@ (class "dropdown d-inline-block ml-2"))
(a (@ (class "oi oi-menu dropdown-toggle no-dropdown-arrow")
(href "#")
(data-toggle "dropdown")
@ -1148,6 +1161,10 @@ $(document).ready(function() {
status
id)
" "
(a (@ (class "oi oi-monitor mr-2")
(style "font-size:0.8em")
(href "/eval/" ,id "/dashboard")
(role "button")))
(a (@ (id "paginate")
(class "oi oi-collapse-down")
(style "font-size:0.7em")
@ -1707,3 +1724,122 @@ text-dark d-flex position-absolute w-100"))
#:labels
'("Free store disk space percentage")
#:colors (list "#3e95cd"))))))))
(define* (evaluation-dashboard evaluation systems
#:key current-system)
(let ((jobs
(format #f "/api/jobs?evaluation=~a&system=~a"
evaluation current-system)))
`((p (@ (class "lead"))
,(format #f "Dasboard evaluation #~a" evaluation))
(form (@ (id "get-dashboard")
(class "row g-3 mb-3")
(action "/eval/" ,evaluation "/dashboard")
(method "GET"))
(div (@ (class "col-auto"))
(select (@ (id "system")
(name "system")
(class "form-control"))
,@(map (lambda (system)
`(option
(@ (value ,system)
,@(if (string=? system current-system)
'((selected))
'()))
,system))
systems)))
(div (@ (class "col-auto"))
(button
(@ (type "submit")
(class "btn btn-primary"))
" Go")))
(script ,(format #f "
function radius(count) {
if (count < 100)
return 15;
else if (count < 1000)
return 10;
else
return 5;
}
function color(status) {
switch (status) {
case -3:
case -2:
return 'gray';
case -1:
return 'orange';
case 0:
return 'green';
case 1:
case 2:
case 3:
case 4:
return 'red';
}
}
function svgWidth() {
var width = d3.select('#content').style('width').slice(0, -2);
return Math.round(Number(width));
}
d3.json('~a').then(function (data) {
var width = svgWidth();
var circle_radius = radius(data.length);
var margin_x = circle_radius;
var margin_y = circle_radius;
var margin_circle_x = 3;
var margin_circle_y = (2.5 * circle_radius);
var circle_count_x =
Math.floor((width - 2 * margin_x) /
((2 * circle_radius) + margin_circle_x));
var height = ((data.length / circle_count_x) *
margin_circle_y) +
circle_radius + 2 * margin_y;
console.log(width);
console.log(height);
var div = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
var svg = d3.select('#content').append('svg')
.attr('width', width)
.attr('height', height);
var circles = svg.append('g')
.selectAll('circle')
.data(data)
.enter()
.append('a')
.attr('xlink:href', d => '/build/' + d.build + '/details')
.append('circle')
.attr('r', circle_radius)
.attr('cx', function(d, i) {
return margin_x +
(i % circle_count_x)
* (circle_radius * 2 + margin_circle_x);
})
.attr('cy', function (d, i) {
return margin_y + Math.floor(i / circle_count_x)
* margin_circle_y;
})
.style('fill', d => color(d.status))
.on('mouseover', function(event, d) {
var circle = d3.select(this)
.style('fill', 'steelblue');
div.style('opacity', .9);
div.html(d.name)
.style('left', (event.pageX + 30) + 'px')
.style('top', (event.pageY - 30) + 'px');
})
.on('mouseout', function(event, d) {
var circle = d3.select(this)
.style('fill', color(d.status));
div.style('opacity', 0);
div.html('')
.style('left', '0px')
.style('top', '0px');
})
});" jobs)))))

View File

@ -15,6 +15,10 @@ body {
padding: 1em 0em 1em;
}
.content-fixed-margin {
margin: 1em 3em;
}
#search input:focus {
width: 500px;
}
@ -66,3 +70,24 @@ a.dropdown-toggle:focus + .dropdown-menu {
background-color: #f5f5f5;
}
#dashboard {
display: block;
margin: auto;
}
div.tooltip {
position: absolute;
opacity:0.8;
z-index:1000;
text-align:left;
border-radius:4px;
-moz-border-radius:4px;
-webkit-border-radius:4px;
padding:8px;
color:#fff;
background-color:#000;
font: 12px sans-serif;
max-width: 1000px;
height: 30px;
}