From 2d79e5286c81aa7d4252330f15d307b816a01e4a Mon Sep 17 00:00:00 2001 From: Scott Schneider Date: Wed, 18 Mar 2015 12:54:37 -0700 Subject: [PATCH] Add a 'weekly' dashboard --- lib/vmpooler/api.rb | 14 +- lib/vmpooler/dashboard.rb | 15 + lib/vmpooler/public/dashboard.css | 4 +- .../lib/stats-vmpooler-numbers-weekly.js | 228 +++++++++++ .../public/lib/stats-vmpooler-weekly.js | 354 ++++++++++++++++++ lib/vmpooler/views/dashboard.erb | 10 + lib/vmpooler/views/dashboard_weekly.erb | 28 ++ lib/vmpooler/views/layout.erb | 10 - 8 files changed, 649 insertions(+), 14 deletions(-) create mode 100644 lib/vmpooler/dashboard.rb create mode 100644 lib/vmpooler/public/lib/stats-vmpooler-numbers-weekly.js create mode 100644 lib/vmpooler/public/lib/stats-vmpooler-weekly.js create mode 100644 lib/vmpooler/views/dashboard_weekly.erb diff --git a/lib/vmpooler/api.rb b/lib/vmpooler/api.rb index 6b8419e..f2f49ca 100644 --- a/lib/vmpooler/api.rb +++ b/lib/vmpooler/api.rb @@ -34,11 +34,19 @@ module Vmpooler end get '/' do - erb :dashboard, locals: { - site_name: $config[:config]['site_name'] || 'vmpooler' - } + redirect to('/dashboard/') end + # Load dashboard components + begin + require "dashboard" + rescue LoadError + require File.expand_path(File.join(File.dirname(__FILE__), 'dashboard')) + end + + use Vmpooler::Dashboard + + # Load API components %w( dashboard reroute v1 ).each do |lib| begin require "api/#{lib}" diff --git a/lib/vmpooler/dashboard.rb b/lib/vmpooler/dashboard.rb new file mode 100644 index 0000000..1f85934 --- /dev/null +++ b/lib/vmpooler/dashboard.rb @@ -0,0 +1,15 @@ +module Vmpooler + class Dashboard < Sinatra::Base + get '/dashboard/?' do + erb :dashboard, locals: { + site_name: $config[:config]['site_name'] || 'vmpooler' + } + end + + get '/dashboard/weekly/?' do + erb :dashboard_weekly, locals: { + site_name: $config[:config]['site_name'] || 'vmpooler' + } + end + end +end diff --git a/lib/vmpooler/public/dashboard.css b/lib/vmpooler/public/dashboard.css index a836794..6c8aae1 100644 --- a/lib/vmpooler/public/dashboard.css +++ b/lib/vmpooler/public/dashboard.css @@ -41,12 +41,14 @@ body, padding-right: 20px; } -#stats-vmpooler-numbers { +#stats-vmpooler-numbers, +#stats-vmpooler-numbers-weekly { position: relative; top: -80px; right: 15px; } +#stats-vmpooler-weekly, #stats-vmpooler-running { margin-bottom: -15px; } diff --git a/lib/vmpooler/public/lib/stats-vmpooler-numbers-weekly.js b/lib/vmpooler/public/lib/stats-vmpooler-numbers-weekly.js new file mode 100644 index 0000000..734b788 --- /dev/null +++ b/lib/vmpooler/public/lib/stats-vmpooler-numbers-weekly.js @@ -0,0 +1,228 @@ +function whichDay( dateString ) { + var daysOfWeek = new Array( 'mon.', 'tues.', 'wed.', 'thur.', 'fri.', 'sat.', 'sun.' ); + return daysOfWeek[ new Date( dateString ).getDay() ]; +} + +Date.prototype.yyyymmdd = function() { + var yyyy = this.getFullYear().toString(); + var mm = (this.getMonth()+1).toString(); + var dd = this.getDate().toString(); + return yyyy + '-' + ( mm[1] ? mm : '0' + mm[0] ) + '-' + ( dd[1] ? dd : '0' + dd[0] ); +}; + +var date_from = new Date(); +date_from.setDate( date_from.getDate() - 6 ); + +var numbers_url = '/summary?from=' + date_from.yyyymmdd(); +var numbers_width = 150; +var numbers_height = 50; + +var stats_vmpooler_numbers__data = {}; +var stats_vmpooler_numbers__svg = {}; + +d3.json( numbers_url, + + function( stats_vmpooler_numbers__data ) { + + ( function tick() { + setTimeout( function() { + var stats_vmpooler_numbers__data__live = ( function() { + var stats_vmpooler_numbers__data__live = null; + + $.ajax( { + 'url': numbers_url, + 'async': false, + 'global': false, + 'dataType': 'json', + 'success': function( data ) { + stats_vmpooler_numbers__data__live = data; + } + } ); + + return stats_vmpooler_numbers__data__live; + } )(); + + $( '#stats-vmpooler-numbers-weekly' ).empty(); + + stats_vmpooler_numbers__svg[ 'boot_average' ] = d3.select( '#stats-vmpooler-numbers-weekly' ) + .append( 'svg' ) + .style( 'margin', '15px 0px 0px 0px' ) + .style( 'padding', '0px 10px 20px 10px' ) + .style( 'float', 'right' ) + .attr( 'width', numbers_width + 'px' ) + .attr( 'height', numbers_height + 'px' ); + + stats_vmpooler_numbers__svg[ 'boot_average' ] + .append( 'text' ) + .text( + ( 'boot time average' ) + ) + .attr( { + 'text-anchor': 'end', + 'x': numbers_width, + 'y': numbers_height, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + + stats_vmpooler_numbers__svg[ 'boot_average' ] + .append( 'text' ) + .text( + ( ( Math.round( stats_vmpooler_numbers__data__live[ 'boot' ][ 'duration' ][ 'average' ] * 10 ) / 10 ) + 's' ) + ) + .attr( { + 'text-anchor': 'end', + 'x': numbers_width, + 'y': '36', + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '50px', + 'letter-spacing': '-0.05em', + 'fill': '#444' + } ); + + stats_vmpooler_numbers__svg[ 'clone_average' ] = d3.select( '#stats-vmpooler-numbers-weekly' ) + .append( 'svg' ) + .style( 'margin', '15px 0px 0px 0px' ) + .style( 'padding', '0px 10px 20px 10px' ) + .style( 'float', 'right' ) + .attr( 'width', numbers_width + 'px' ) + .attr( 'height', numbers_height + 'px' ); + + stats_vmpooler_numbers__svg[ 'clone_average' ] + .append( 'text' ) + .text( + ( 'clone time average' ) + ) + .attr( { + 'text-anchor': 'end', + 'x': numbers_width, + 'y': numbers_height, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + + stats_vmpooler_numbers__svg[ 'clone_average' ] + .append( 'text' ) + .text( + ( ( Math.round( stats_vmpooler_numbers__data__live[ 'clone' ][ 'duration' ][ 'average' ] * 10 ) / 10 ) + 's' ) + ) + .attr( { + 'text-anchor': 'end', + 'x': numbers_width, + 'y': '36', + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '50px', + 'letter-spacing': '-0.05em', + 'fill': '#444' + } ); + + stats_vmpooler_numbers__data[ 'clone_best_day' ] = ''; + stats_vmpooler_numbers__data[ 'clone_total' ] = []; + + stats_vmpooler_numbers__data__live[ 'daily' ].sort().map( + function( day ) { + stats_vmpooler_numbers__data[ 'clone_total' ].push( parseInt( day[ 'clone' ][ 'count' ][ 'total' ] ) ); + } + ); + + stats_vmpooler_numbers__data__live[ 'daily' ].sort().map( + function( day ) { + if ( day[ 'clone' ][ 'count' ][ 'total' ] == Math.max.apply( Math, stats_vmpooler_numbers__data[ 'clone_total' ] ) ) { + stats_vmpooler_numbers__data[ 'clone_best_day' ] = day[ 'date' ]; + } + } + ); + + stats_vmpooler_numbers__svg[ 'best_day' ] = d3.select( '#stats-vmpooler-numbers-weekly' ) + .append( 'svg' ) + .style( 'margin', '15px 0px 0px 0px' ) + .style( 'padding', '0px 10px 20px 10px' ) + .style( 'float', 'right' ) + .attr( 'width', numbers_width + 'px' ) + .attr( 'height', numbers_height + 'px' ); + + stats_vmpooler_numbers__svg[ 'best_day' ] + .append( 'text' ) + .text( + ( 'most clones' ) + ) + .attr( { + 'text-anchor': 'end', + 'x': numbers_width, + 'y': numbers_height, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + + stats_vmpooler_numbers__svg[ 'best_day' ] + .append( 'text' ) + .text( + ( whichDay( stats_vmpooler_numbers__data[ 'clone_best_day' ] ) ) + ) + .attr( { + 'text-anchor': 'end', + 'x': numbers_width, + 'y': '36', + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '50px', + 'letter-spacing': '-0.05em', + 'fill': '#444' + } ); + + + stats_vmpooler_numbers__svg[ 'clone_total' ] = d3.select( '#stats-vmpooler-numbers-weekly' ) + .append( 'svg' ) + .style( 'margin', '15px 0px 0px 0px' ) + .style( 'padding', '0px 10px 20px 10px' ) + .style( 'float', 'right' ) + .attr( 'width', numbers_width + 'px' ) + .attr( 'height', numbers_height + 'px' ); + + stats_vmpooler_numbers__svg[ 'clone_total' ] + .append( 'text' ) + .text( + ( 'cloned this week' ) + ) + .attr( { + 'text-anchor': 'end', + 'x': numbers_width, + 'y': numbers_height, + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + + stats_vmpooler_numbers__svg[ 'clone_total' ] + .append( 'text' ) + .text( + ( stats_vmpooler_numbers__data__live[ 'clone' ][ 'count' ][ 'total' ].toLocaleString() ) + ) + .attr( { + 'text-anchor': 'end', + 'x': numbers_width, + '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/lib/vmpooler/public/lib/stats-vmpooler-weekly.js b/lib/vmpooler/public/lib/stats-vmpooler-weekly.js new file mode 100644 index 0000000..b315d93 --- /dev/null +++ b/lib/vmpooler/public/lib/stats-vmpooler-weekly.js @@ -0,0 +1,354 @@ +Date.prototype.yyyymmdd = function() { + var yyyy = this.getFullYear().toString(); + var mm = (this.getMonth()+1).toString(); + var dd = this.getDate().toString(); + return yyyy + '-' + ( mm[1] ? mm : '0' + mm[0] ) + '-' + ( dd[1] ? dd : '0' + dd[0] ); +}; + +var date_from = new Date(); +date_from.setDate( date_from.getDate() - 6 ); + +var clone_weekly_url = '/summary?from=' + date_from.yyyymmdd(); +var clone_weekly_height = 160; + +var colorscale = d3.scale.category20(); +var color = {}; + +var stats_vmpooler_weekly__data = {}; +var stats_vmpooler_weekly__svg = {}; + +d3.json( clone_weekly_url, + + function( stats_vmpooler_weekly__data ) { + + var stats_vmpooler_weekly__data__live = ( function() { + var stats_vmpooler_weekly__data__live = null; + + $.ajax( { + 'url': clone_weekly_url, + 'async': false, + 'global': false, + 'dataType': 'json', + 'success': function( data ) { + stats_vmpooler_weekly__data__live = data; + } + } ); + + return stats_vmpooler_weekly__data__live; + } )(); + + var clone_weekly_width = document.getElementById( 'stats-vmpooler-weekly-clone-count-pool' ).offsetWidth - 25; + + // Empty the divs + $( '#stats-vmpooler-weekly-clone-count-pool' ).empty(); + $( '#stats-vmpooler-weekly-daily' ).empty(); + + // Per-pool clone count bar graph + stats_vmpooler_weekly__svg[ 'clone_count__pool' ] = d3.select( '#stats-vmpooler-weekly-clone-count-pool' ) + .append( 'svg' ) + .style( 'margin', '-40px 0px 10px 0px' ) + .style( 'border-bottom', 'solid 1px #888' ) + .attr( { + 'width': clone_weekly_width, + 'height': clone_weekly_height + } ); + + // Define a background texture to apply to SVGs + defs = stats_vmpooler_weekly__svg[ 'clone_count__pool' ].append( 'svg:defs' ); + + defs.append( 'svg:pattern' ) + .attr( { + 'id': 'background', + 'patternUnits': 'userSpaceOnUse', + 'width': '500px', + 'height': '500px' + } ) + .append( 'svg:image' ) + .attr( { + 'xlink:href': '/img/textured_paper.png', + 'x': 0, + 'y': 0, + 'width': '500px', + 'height': '500px' + } ); + + + stats_vmpooler_weekly__data[ 'clone_count__max' ] = 0 + stats_vmpooler_weekly__data[ 'pool_maj__count' ] = 0 + + Object.keys( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ] ).sort().map( + function( pool ) { + + // Determine 'major' pool (eg. 'centos' for 'centos-6-x86_64') + var pool_maj = 'unknown'; + if ( pool.match( /^(.+?)\-/ ) ) { pool_maj = pool.match( /^(.+?)\-/ )[ 1 ]; } + + // Generate fill color for pool_maj + if ( ! color[ pool_maj ] ) { + color[ pool_maj ] = colorscale( stats_vmpooler_weekly__data[ 'pool_maj__count' ] ); + stats_vmpooler_weekly__data[ 'pool_maj__count' ]++; + } + + // Is this the most clones? + if ( parseInt( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ][ pool ][ 'total' ] ) > stats_vmpooler_weekly__data[ 'clone_count__max' ] ) { + stats_vmpooler_weekly__data[ 'clone_count__max' ] = parseInt( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ][ pool ][ 'total' ] ) + } + + } + ); + + // Set up dynamic x and y domain scales + stats_vmpooler_weekly__data[ 'y__clone_count__pool' ] = d3.scale.linear().domain( [ 0, stats_vmpooler_weekly__data[ 'clone_count__max' ] ] ).range( [ clone_weekly_height, 0 ] ); + stats_vmpooler_weekly__data[ 'pool__count' ] = 0; + + Object.keys( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ] ).sort().map( + function( pool ) { + + // Determine 'major' pool (eg. 'centos' for 'centos-6-x86_64') + var pool_maj = 'unknown'; + if ( pool.match( /^(.+?)\-/ ) ) { pool_maj = pool.match( /^(.+?)\-/ )[ 1 ]; } + + // Append a bar to the per-pool clone count bar graph + stats_vmpooler_weekly__svg[ 'clone_count__pool' ] + .append( 'rect' ) + .attr( { + 'x': stats_vmpooler_weekly__data[ 'pool__count' ] * ( clone_weekly_width / Object.keys( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ] ).length ), + 'y': stats_vmpooler_weekly__data[ 'y__clone_count__pool' ]( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ][ pool ][ 'total' ] ), + 'width': ( clone_weekly_width / Object.keys( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ] ).length ) - 2, + 'height': clone_weekly_height, + 'fill': color[ pool_maj ], + 'opacity': '0.75' + } ); + + stats_vmpooler_weekly__svg[ 'clone_count__pool' ] + .append( 'rect' ) + .attr( { + 'x': stats_vmpooler_weekly__data[ 'pool__count' ] * ( clone_weekly_width / Object.keys( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ] ).length ), + 'y': stats_vmpooler_weekly__data[ 'y__clone_count__pool' ]( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ][ pool ][ 'total' ] ), + 'width': ( clone_weekly_width / Object.keys( stats_vmpooler_weekly__data__live[ 'clone' ][ 'count' ][ 'pool' ] ).length ) - 2, + 'height': clone_weekly_height, + 'fill': 'url( #background )', + 'opacity': '0.50' + } ); + + // Append a key to the per-pool clone count bar graph + if ( ! stats_vmpooler_weekly__svg[ 'clone_count__pool__key__' + pool_maj ] ) { + stats_vmpooler_weekly__svg[ 'clone_count__pool__key__' + pool_maj ] = d3.select( '#stats-vmpooler-weekly-clone-count-pool' ) + .append( 'svg' ) + .style( 'padding', '0px 10px 10px 10px' ) + .attr( { + 'width': '130px', + 'height': '12px' + } ); + + stats_vmpooler_weekly__svg[ 'clone_count__pool__key__' + pool_maj ] + .append( 'rect' ) + .attr( { + 'x': '5', + 'y': '3', + 'width': '10', + 'height': '10', + 'fill': color[ pool_maj ], + 'opacity': '0.75' + } ); + + stats_vmpooler_weekly__svg[ 'clone_count__pool__key__' + pool_maj ] + .append( 'rect' ) + .attr( { + 'x': '5', + 'y': '3', + 'width': '10', + 'height': '10', + 'fill': 'url( #background )', + 'opacity': '0.50' + } ); + + stats_vmpooler_weekly__svg[ 'clone_count__pool__key__' + pool_maj ] + .append( 'text' ) + .text( + ( pool_maj ) + ) + .attr( { + 'x': '20', + 'y': '12', + 'font-face': '\'PT Sans\', sans-serif', + 'font-size': '12px', + 'font-weight': 'bold', + 'fill': '#888' + } ); + } + + stats_vmpooler_weekly__data[ 'pool__count' ]++; + + } + ); + +// + // Daily clone count, clone average, and boot average graphs + for ( graph in graphs = [ 'clone_count__day', 'clone_avg__day', 'boot_avg__day' ] ) { + stats_vmpooler_weekly__svg[ graphs[ graph ] ] = d3.select( '#stats-vmpooler-weekly-daily' ) + .append( 'svg' ) + .style( 'margin', '20px 0px 0px 0px' ) + .style( 'padding', '0px 25px 25px 0px' ) + .style( 'float', 'left' ) + .attr( 'width', ( clone_weekly_width / 3 ) - 20 ) + .attr( 'height', clone_weekly_height ); + + stats_vmpooler_weekly__svg[ graphs[ graph ] ] + .append( 'g' ) + .attr( 'class', 'x tick' ) + .attr( 'transform', 'translate( 0,' + ( clone_weekly_height ) + ')' ) + .call( + d3.svg.axis() + .scale( d3.scale.linear().domain( [ 0, 6 ] ).range( [ 0, ( clone_weekly_width / 3 ) - 20 ] ) ) + .ticks( 7 ) + .tickSize( -clone_weekly_height ) + .outerTickSize( 0 ) + .tickFormat( '' ) + .tickSubdivide( true ) + .orient( 'bottom' ) + ); + } +// + stats_vmpooler_weekly__data[ 'clone_total' ] = []; + stats_vmpooler_weekly__data[ 'clone_avg' ] = []; + stats_vmpooler_weekly__data[ 'boot_avg' ] = []; + + stats_vmpooler_weekly__data[ 'clone_max' ] = 0; + + stats_vmpooler_weekly__data__live[ 'daily' ].sort().map( + function( day ) { + stats_vmpooler_weekly__data[ 'clone_total' ].push( parseInt( day[ 'clone' ][ 'count' ][ 'total' ] ) ); + stats_vmpooler_weekly__data[ 'clone_avg' ].push( day[ 'clone' ][ 'duration' ][ 'average' ] ); + stats_vmpooler_weekly__data[ 'boot_avg' ].push( day[ 'boot' ][ 'duration' ][ 'average' ] ); + + if ( parseInt( day[ 'clone' ][ 'count' ][ 'total' ] ) > stats_vmpooler_weekly__data[ 'clone_max' ] ) { + stats_vmpooler_weekly__data[ 'clone_max' ] = parseInt( day[ 'clone' ][ 'count' ][ 'total' ] ) + } + } + ); + + var x = d3.scale.linear().domain( [ 0, 6 ] ).range( [ 0, ( clone_weekly_width / 3 ) - 20 ] ); + + var y_clone_total = d3.scale.linear().domain( [ 0, stats_vmpooler_weekly__data[ 'clone' ][ 'count' ][ 'max' ] ] ).range( [ clone_weekly_height, 0 ] ); + var y_clone_avg = d3.scale.linear().domain( [ 0, Math.max.apply( Math, stats_vmpooler_weekly__data[ 'clone_avg' ] ) ] ).range( [ clone_weekly_height, 0 ] ); + var y_boot_avg = d3.scale.linear().domain( [ 0, Math.max.apply( Math, stats_vmpooler_weekly__data[ 'boot_avg' ] ) ] ).range( [ clone_weekly_height, 0 ] ); + + var area_clone_total = d3.svg.area() + .interpolate( 'linear' ) + .x( function( d, i ) { return x( i ); } ) + .y0( clone_weekly_height ) + .y1( function( d ) { return y_clone_total( d ); } ); + + var area_clone_avg = d3.svg.area() + .interpolate( 'linear' ) + .x( function( d, i ) { return x( i ); } ) + .y0( clone_weekly_height ) + .y1( function( d ) { return y_clone_avg( d ); } ); + + var area_boot_avg = d3.svg.area() + .interpolate( 'linear' ) + .x( function( d, i ) { return x( i ); } ) + .y0( clone_weekly_height ) + .y1( function( d ) { return y_boot_avg( d ); } ); + + stats_vmpooler_weekly__svg[ 'clone_count__day' ] + .append( 'path' ) + .attr( { + 'class': 'area', + 'fill': 'seagreen', + 'opacity': '0.75', + 'd': area_clone_total( stats_vmpooler_weekly__data[ 'clone_total' ] ) + } ); + + stats_vmpooler_weekly__svg[ 'clone_count__day' ] + .append( 'path' ) + .attr( { + 'class': 'area', + 'fill': 'url( #background )', + 'opacity': '0.50', + 'd': area_clone_total( stats_vmpooler_weekly__data[ 'clone_total' ] ) + } ); + + stats_vmpooler_weekly__svg[ 'clone_count__day' ] + .append( 'text' ) + .text( + ( 'daily provision count' ) + ) + .attr( { + 'x': '5', + 'y': clone_weekly_height + 20, + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '12px', + 'fill': '#888' + } ); + + stats_vmpooler_weekly__svg[ 'clone_avg__day' ] + .append( 'path' ) + .attr( { + 'class': 'area', + 'fill': 'steelblue', + 'opacity': '0.75', + 'd': area_clone_avg( stats_vmpooler_weekly__data[ 'clone_avg' ] ) + } ); + + stats_vmpooler_weekly__svg[ 'clone_avg__day' ] + .append( 'path' ) + .attr( { + 'class': 'area', + 'fill': 'url( #background )', + 'opacity': '0.50', + 'd': area_clone_avg( stats_vmpooler_weekly__data[ 'clone_avg' ] ) + } ); + + stats_vmpooler_weekly__svg[ 'clone_avg__day' ] + .append( 'text' ) + .text( + ( 'clone time ( average )' ) + ) + .attr( { + 'x': '5', + 'y': clone_weekly_height + 20, + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '12px', + 'fill': '#888' + } ); + + stats_vmpooler_weekly__svg[ 'boot_avg__day' ] + .append( 'path' ) + .attr( { + 'class': 'area', + 'fill': 'crimson', + 'opacity': '0.75', + 'd': area_boot_avg( stats_vmpooler_weekly__data[ 'boot_avg' ] ) + } ); + + stats_vmpooler_weekly__svg[ 'boot_avg__day' ] + .append( 'path' ) + .attr( { + 'class': 'area', + 'fill': 'url( #background )', + 'opacity': '0.50', + 'd': area_boot_avg( stats_vmpooler_weekly__data[ 'boot_avg' ] ) + } ); + + stats_vmpooler_weekly__svg[ 'boot_avg__day' ] + .append( 'text' ) + .text( + ( 'boot time ( average )' ) + ) + .attr( { + 'x': '5', + 'y': clone_weekly_height + 20, + 'font-face': '\'PT Sans\', sans-serif', + 'font-weight': 'bold', + 'font-size': '12px', + 'fill': '#888' + } ); +// + + } +); + diff --git a/lib/vmpooler/views/dashboard.erb b/lib/vmpooler/views/dashboard.erb index 888ba29..6cb6385 100644 --- a/lib/vmpooler/views/dashboard.erb +++ b/lib/vmpooler/views/dashboard.erb @@ -1,3 +1,13 @@ + +
VMs running tests
diff --git a/lib/vmpooler/views/dashboard_weekly.erb b/lib/vmpooler/views/dashboard_weekly.erb new file mode 100644 index 0000000..3c7ff6a --- /dev/null +++ b/lib/vmpooler/views/dashboard_weekly.erb @@ -0,0 +1,28 @@ + + + + +
Weekly clone counts by pool
+ +
+
Loading data... +
+ + + +
Weekly trends by day
+ +
+
Loading data... +
+ + + diff --git a/lib/vmpooler/views/layout.erb b/lib/vmpooler/views/layout.erb index 199d930..ad654d7 100644 --- a/lib/vmpooler/views/layout.erb +++ b/lib/vmpooler/views/layout.erb @@ -16,16 +16,6 @@
- - <%= yield %>