diff --git a/public/dashboard.css b/public/dashboard.css new file mode 100644 index 0000000..12fc3fc --- /dev/null +++ b/public/dashboard.css @@ -0,0 +1,138 @@ +body, +#content { + margin-top: 20px; + width: 960px; + margin-left: auto; + margin-right: auto; +} + +#header { + border-bottom: solid 1px #888; + margin-bottom: 20px; +} + +#header .logo, +#header .text { + margin-left: 30px; + margin-bottom: 5px; + display: inline-block; +} + +#header .text { + float: right; + position: relative; + right: 30px; + top: 100px; + font: 50px 'PT Sans', sans-serif; + letter-spacing: -0.05em; + color: #444; +} + +.label { + text-transform: uppercase; + text-indent: 25px; + font: 12px 'PT Sans', sans-serif; + font-weight: bold; + line-height: 20px; + color: #888; + border-bottom: solid 1px #888; +} + +.spinner { + background-image: url( '/img/spinner.svg' ); + width: 15px; + height: 15px; + background-size: contain; + opacity: .7; + float: left; + margin-right: 5px; + -webkit-animation:rotate 1.5s infinite steps( 12 ); + -moz-animation:rotate 1.5s infinite steps( 12 ); + -ms-animation:rotate 1.5s infinite steps( 12 ); + -o-animation:rotate 1.5s infinite steps( 12 ); + animation:rotate 1.5s infinite steps( 12 ); +} +@keyframes 'rotate' { + from { + -webkit-transform: rotate( 0deg ); + -moz-transform: rotate( 0deg ); + -o-transform: rotate( 0deg ); + -ms-transform: rotate( 0deg ); + transform: rotate( 0deg ); + } + to { + -webkit-transform: rotate( 360deg ); + -moz-transform: rotate( 360deg ); + -o-transform: rotate( 360deg ); + -ms-transform: rotate( 360deg ); + transform: rotate( 360deg ); + } +} +@-moz-keyframes rotate { + from { + -moz-transform: rotate( 0deg ); + transform: rotate( 0deg ); + } + to { + -moz-transform: rotate( 360deg ); + transform: rotate( 360deg ); + } +} +@-webkit-keyframes 'rotate' { + from { + -webkit-transform: rotate( 0deg ); + transform: rotate( 0deg ); + } + to { + -webkit-transform: rotate( 360deg ); + transform: rotate( 360deg ); + } +} +@-ms-keyframes 'rotate' { + from { + -ms-transform: rotate( 0deg ); + transform: rotate( 0deg ); + } + to { + -ms-transform: rotate( 360deg ); + transform: rotate( 360deg ); + } +} +@-o-keyframes 'rotate' { + from { + -o-transform: rotate( 0deg ); + transform: rotate( 0deg ); + } + to { + -o-transform: rotate( 360deg ); + transform: rotate( 360deg ); + } +} + +.module { + margin-top: 5px; + margin-left: 25px; + font: 13px 'PT Sans', sans-serif; + line-height: 20px; + color: #888; +} + +.line { + fill: none; +} + +.area { + stroke-width: 0; +} + +.axis path, +.axis line { + fill: none; + stroke: #fff; + shape-rendering: crispEdges; +} + +.tick { + fill: none; + stroke: #eee; +} diff --git a/public/img/logo.jpg b/public/img/logo.jpg new file mode 100644 index 0000000..5f4b086 Binary files /dev/null and b/public/img/logo.jpg differ diff --git a/public/img/spinner.svg b/public/img/spinner.svg new file mode 100644 index 0000000..52de3bc --- /dev/null +++ b/public/img/spinner.svg @@ -0,0 +1,38 @@ + + + diff --git a/public/lib/stats-vcloud-numbers.js b/public/lib/stats-vcloud-numbers.js new file mode 100644 index 0000000..d308a56 --- /dev/null +++ b/public/lib/stats-vcloud-numbers.js @@ -0,0 +1,183 @@ +var numbers_url = '/dashboard/stats/vcloud/numbers'; +var numbers_width = 130; +var numbers_height = 50; + +var stats_vcloud_numbers__data = {}; +var stats_vcloud_numbers__svg = {}; + +d3.json( numbers_url, + + function( stats_vcloud_numbers__data ) { + + ( function tick() { + setTimeout( function() { + var stats_vcloud_numbers__data__live = ( function() { + var stats_vcloud_numbers__data__live = null; + + $.ajax( { + 'url': numbers_url, + 'async': false, + 'global': false, + 'dataType': 'json', + 'success': function( data ) { + stats_vcloud_numbers__data__live = data; + } + } ); + + return stats_vcloud_numbers__data__live; + } )(); + + $( '#stats-vcloud-numbers' ).empty(); + + stats_vcloud_numbers__svg[ 'ready' ] = d3.select( '#stats-vcloud-numbers' ) + .append( 'svg' ) + .style( 'margin', '10px 25px 0px 0px' ) + .style( 'padding', '0px 0px 20px 0px' ) + .attr( 'width', numbers_width + 'px' ) + .attr( 'height', numbers_height + 'px' ); + + stats_vcloud_numbers__svg[ 'ready' ] + .append( 'text' ) + .text( + ( 'ready and waiting' ) + ) + .attr( { + 'x': '5', + 'y': numbers_height, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + + stats_vcloud_numbers__svg[ 'ready' ] + .append( 'text' ) + .text( + ( stats_vcloud_numbers__data__live[ 'ready' ] ) + ) + .attr( { + 'x': '0', + 'y': '36', + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '50px', + 'letter-spacing': '-0.05em', + 'fill': '#444' + } ); + + stats_vcloud_numbers__svg[ 'pending' ] = d3.select( '#stats-vcloud-numbers' ) + .append( 'svg' ) + .style( 'margin', '10px 25px 0px 0px' ) + .style( 'padding', '0px 0px 20px 0px' ) + .attr( 'width', numbers_width + 'px' ) + .attr( 'height', numbers_height + 'px' ); + + stats_vcloud_numbers__svg[ 'pending' ] + .append( 'text' ) + .text( + ( 'being built' ) + ) + .attr( { + 'x': '5', + 'y': numbers_height, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + + stats_vcloud_numbers__svg[ 'pending' ] + .append( 'text' ) + .text( + ( stats_vcloud_numbers__data__live[ 'pending' ] ) + ) + .attr( { + 'x': '0', + 'y': '36', + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '50px', + 'letter-spacing': '-0.05em', + 'fill': '#444' + } ); + + stats_vcloud_numbers__svg[ 'running' ] = d3.select( '#stats-vcloud-numbers' ) + .append( 'svg' ) + .style( 'margin', '10px 25px 0px 0px' ) + .style( 'padding', '0px 0px 20px 0px' ) + .attr( 'width', numbers_width + 'px' ) + .attr( 'height', numbers_height + 'px' ); + + stats_vcloud_numbers__svg[ 'running' ] + .append( 'text' ) + .text( + ( 'running tests' ) + ) + .attr( { + 'x': '5', + 'y': numbers_height, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + + stats_vcloud_numbers__svg[ 'running' ] + .append( 'text' ) + .text( + ( stats_vcloud_numbers__data__live[ 'running' ] ) + ) + .attr( { + 'x': '0', + 'y': '36', + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '50px', + 'letter-spacing': '-0.05em', + 'fill': '#444' + } ); + + stats_vcloud_numbers__svg[ 'completed' ] = d3.select( '#stats-vcloud-numbers' ) + .append( 'svg' ) + .style( 'margin', '10px 25px 0px 0px' ) + .style( 'padding', '0px 0px 20px 0px' ) + .attr( 'width', numbers_width + 'px' ) + .attr( 'height', numbers_height + 'px' ); + + stats_vcloud_numbers__svg[ 'completed' ] + .append( 'text' ) + .text( + ( 'waiting to die' ) + ) + .attr( { + 'x': '5', + 'y': numbers_height, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + + stats_vcloud_numbers__svg[ 'completed' ] + .append( 'text' ) + .text( + ( stats_vcloud_numbers__data__live[ 'completed' ] ) + ) + .attr( { + 'x': '0', + 'y': '36', + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '50px', + 'letter-spacing': '-0.05em', + 'fill': '#444' + } ); + + tick(); + }, 5000 ); + } )(); + + } + +); + diff --git a/public/lib/stats-vcloud-pool.js b/public/lib/stats-vcloud-pool.js new file mode 100644 index 0000000..d5c47cb --- /dev/null +++ b/public/lib/stats-vcloud-pool.js @@ -0,0 +1,166 @@ +var pool_url = '/dashboard/stats/vcloud/pool'; +var pool_width = 130; +var pool_height = 85; + +var stats_vcloud_pool__data = {}; +var stats_vcloud_pool__svg = {}; + +d3.json( pool_url+'?history=1', + + function( stats_vcloud_pool__data ) { + + var stats_vcloud_pool__data__keys = []; + + for ( var key in stats_vcloud_pool__data ) { + stats_vcloud_pool__data__keys.push( key ); + } + + stats_vcloud_pool__data__keys.sort().map( + function( pool ) { + stats_vcloud_pool__data[ pool ][ 'r' ] = stats_vcloud_pool__data[ pool ][ 'history' ]; + } + ); + + ( function tick() { + setTimeout( function() { + var stats_vcloud_pool__data__live = ( function() { + var stats_vcloud_pool__data__live = null; + + $.ajax( { + 'url': pool_url, + 'async': false, + 'global': false, + 'dataType': 'json', + 'success': function( data ) { + stats_vcloud_pool__data__live = data; + } + } ); + + return stats_vcloud_pool__data__live; + } )(); + + $( '#stats-vcloud-pool' ).empty(); + + stats_vcloud_pool__data__keys.sort().map( + function( pool ) { + var x = d3.scale.linear().domain( [ 0, 500 ] ).range( [ 0, pool_width ] ); + var y = d3.scale.linear().domain( [ parseInt( stats_vcloud_pool__data__live[ pool ][ 'size' ] ), 0 ] ).range( [ 0, pool_height - 15 ] ); + + var area = d3.svg.area() + .interpolate( 'basis' ) + .x( function( d, i ) { return x( i ); } ) + .y0( pool_height - 15 ) + .y1( function( d ) { return y( d ); } ); + + var path = d3.svg.line() + .interpolate( 'basis' ) + .x( function( d, i ) { return x( i ); } ) + .y( function( d ) { return y( d ); } ); + + stats_vcloud_pool__data[ pool ][ 'r' ].push( parseInt( stats_vcloud_pool__data__live[ pool ][ 'ready' ] ) ); + + var pool_current = stats_vcloud_pool__data[ pool ][ 'r' ].slice( -1 )[ 0 ]; + var pool_size = stats_vcloud_pool__data[ pool ][ 'size' ] + var pool_pct = Math.floor( ( pool_current / pool_size ) * 100 ); + + var statuscolor = '#78a830'; + if ( pool_pct < 50 ) { statuscolor = '#f0a800'; } + if ( pool_pct < 25 ) { statuscolor = '#d84830'; } + + stats_vcloud_pool__svg[ pool ] = d3.select( '#stats-vcloud-pool' ) + .append( 'svg' ) + .style( 'margin', '15px 25px 0px 0px' ) + .style( 'padding', '0px 0px 20px 0px' ) + .attr( 'width', pool_width ) + .attr( 'height', pool_height ); + + stats_vcloud_pool__svg[ pool ] + .append( 'g' ) + .attr( 'class', 'x tick' ) + .attr( 'transform', 'translate( 0,' + ( pool_height - 15 ) + ')' ) + .call( + d3.svg.axis() + .scale( x ) + .ticks( 4 ) + .tickSize( -pool_height ) + .outerTickSize( 0 ) + .tickFormat( '' ) + .tickSubdivide( true ) + .orient( 'bottom' ) + ); + + stats_vcloud_pool__svg[ pool ] + .append( 'text' ) + .text( + ( pool ) + ) + .attr( { + 'x': '5', + 'y': pool_height - 2, + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '12px', + 'fill': '#888' + } ); + + stats_vcloud_pool__svg[ pool ] + .append( 'text' ) + .text( + ( pool_pct + '%' ) + ) + .attr( { + 'x': '5', + 'y': pool_height - 20, + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '12px', + 'letter-spacing': '-0.05em', + 'fill': '#888' + } ); + + stats_vcloud_pool__svg[ pool ] + .append( 'text' ) + .text( + ( '( ' ) + + ( pool_current ) + + ( '/' ) + + ( pool_size ) + + ( ' )' ) + ) + .attr( { + 'x': 40, + 'y': pool_height - 20, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'letter-spacing': '-0.05em', + 'fill': '#888' + } ); + + stats_vcloud_pool__svg[ pool ] + .append( 'path' ) + .attr( 'class', 'area' ) + .attr( 'fill', statuscolor ) + .attr( 'opacity', '0.25' ) + .attr( 'd', area( stats_vcloud_pool__data[ pool ][ 'r' ] ) ); + + stats_vcloud_pool__svg[ pool ] + .append( 'path' ) + .attr( 'class', 'line' ) + .attr( 'stroke', statuscolor ) + .attr( 'stroke-width', '1' ) + .attr( 'd', path( stats_vcloud_pool__data[ pool ][ 'r' ] ) ); + + if ( stats_vcloud_pool__data[ pool ][ 'r' ].length > 500 ) { + stats_vcloud_pool__data[ pool ][ 'r' ].shift(); + } + } + ) + + tick(); + }, 5000 ); + } )(); + + } + +); + diff --git a/views/dashboard.erb b/views/dashboard.erb new file mode 100644 index 0000000..8d8b58b --- /dev/null +++ b/views/dashboard.erb @@ -0,0 +1,21 @@ + + +