First heatmap correct integration

This commit is contained in:
Fabrice Marsaud
2015-08-27 14:30:54 +02:00
parent 916b2363d9
commit 2659393f33
3 changed files with 254 additions and 181 deletions

View File

@@ -9,9 +9,9 @@ import foreach from 'lodash.foreach'
import xoApi from 'xo-api'
import xoServices from 'xo-services'
import sunburstChart from 'xo-sunburst-d3'
// import treemapChart from 'xo-treemap-d3'
import weekheatmap from 'xo-week-heatmap'
import 'xo-sunburst-d3'
// import 'xo-treemap-d3'
import'xo-week-heatmap'
import view from './view'
@@ -32,28 +32,84 @@ export default angular.module('dashboard.dataviz', [
template: view
})
})
.controller('Dataviz',function($scope,$interval){
// console.log(' in main ')
$scope.charts={
heatmap:null
}
$interval(
function(){
var values = [];
for (var i = 0 ;i < 220 ; i ++){
values.push({
value:Math.random()*1500-750,
date:Date.now()+ i*60*60*1000
})
}
$scope.charts.heatmap = values;
},5000
)
.filter('underStat', () => {
let isUnderStat = object => object.type === 'host' || object.type === 'VM'
return objects => filter(objects, isUnderStat)
})
.controller('DatavizStorageHierarchical', function DatavizStorageHierarchical(xoApi, $scope, $timeout,$interval, $state, bytesToSizeFilter) {
.controller('Dataviz', function ($scope, $interval, xo, xoApi) {
// console.log(' in main ')
this.charts = {
heatmap: null
}
this.objects = xoApi.all
this.prepareMetrics = function (id) {
this.metrics = undefined
if (!id) {
} else {
this.loadingMetrics = true
xo.vm.refreshStats('e87f47c3-0057-69a2-72c8-6a5df168af43', 2) // 2: week granularity (7 * 24 hours)
.then(result => {
const metrics = []
foreach(result.cpus, (values, metricKey) => {
const mapValues = []
foreach(values, (value, key) => {
mapValues.push({
value: +value,
date: +result.date[key] * 1000
})
})
metrics.push({
key: 'CPU ' + metricKey,
values: mapValues
})
})
foreach(result.vifs, (values, metricKey) => {
const mapValues = []
foreach(values, (value, key) => {
mapValues.push({
value: +value,
date: +result.date[key] * 1000
})
})
metrics.push({
key: '#' + Math.floor(metricKey / 2) + ' ' + (metricKey % 2 ? 'out' : 'in'),
values: mapValues
})
})
foreach(result.xvds, (values, key) => {
const mapValues = []
foreach(values, (value, key) => {
mapValues.push({
value: +value,
date: +result.date[key] * 1000
})
})
metrics.push({
key: 'xvd' + String.fromCharCode(Math.floor(key / 2) + 97) + ' ' + (key % 2 ? 'write' : 'read'),
values: mapValues
})
})
this.loadingMetrics = false
this.metrics = metrics
})
}
}
// $interval(
// function(){
// var values = [];
// for (var i = 0 ;i < 220 ; i ++){
// values.push({
// value:Math.random()*1500-750,
// date:Date.now()+ i*60*60*1000
// })
// }
// $scope.example = values;
// },5000
// )
})
.controller('DatavizStorageHierarchical', function DatavizStorageHierarchical (xoApi, $scope, $timeout, $interval, $state, bytesToSizeFilter) {
$scope.charts = {
selected: {},
data: {
@@ -61,65 +117,63 @@ export default angular.module('dashboard.dataviz', [
children: []
},
click: function (d) {
if(d.non_clickable){
return ;
if (d.non_clickable) {
return
}
switch(d.type){
switch (d.type) {
case 'pool':
$state.go('pools_view',{id: d.id});
break;
$state.go('pools_view', {id: d.id})
break
case 'host':
$state.go('hosts_view',{id: d.id});
break;
$state.go('hosts_view', {id: d.id})
break
case 'srs':
$state.go('SRs_view',{id: d.id});
break;
$state.go('SRs_view', {id: d.id})
break
}
}
}
function populateChartsData() {
function populateChartsData () {
function populatestorage (root, container_id) {
let srs = filter(xoApi.getIndex('srsByContainer')[container_id], (one_srs)=>one_srs.SR_type !== 'iso' && one_srs.SR_type !== 'udev')
function populatestorage(root,container_id){
let srs = filter(xoApi.getIndex('srsByContainer')[container_id] , (one_srs)=>one_srs.SR_type !== 'iso' && one_srs.SR_type !== 'udev')
foreach(srs, function(one_srs){
let srs_used_size=0,
srs_storage = {
foreach(srs, function (one_srs) {
let srs_used_size = 0
const srs_storage = {
name: one_srs.name_label,
id: one_srs.id,
children: [],
size:one_srs.size,
textSize:bytesToSizeFilter(one_srs.size),
type:'srs'
size: one_srs.size,
textSize: bytesToSizeFilter(one_srs.size),
type: 'srs'
}
root.size+=one_srs.size
foreach(one_srs.VDIs, function(vdi_id){
root.size += one_srs.size
foreach(one_srs.VDIs, function (vdi_id) {
let vdi = xoApi.get(vdi_id)
if(vdi && vdi.name_label.indexOf('.iso') === -1){
let vdi_storage={
if (vdi && vdi.name_label.indexOf('.iso') === -1) {
let vdi_storage = {
name: vdi.name_label,
id: vdi_id,
size: vdi.size,
textSize : bytesToSizeFilter(vdi.size),
type:'vdi',
non_clickable:true
textSize: bytesToSizeFilter(vdi.size),
type: 'vdi',
non_clickable: true
}
srs_used_size+=vdi.size
srs_used_size += vdi.size
srs_storage.children.push(vdi_storage)
}
})
if(one_srs.size > srs_used_size){// some unallocated space
if (one_srs.size > srs_used_size) {// some unallocated space
srs_storage.children.push({
color:'white',
color: 'white',
name: 'Free',
id: 'free'+one_srs.id,
size: one_srs.size-srs_used_size ,
textSize : bytesToSizeFilter( one_srs.size-srs_used_size),
type:'vdi',
non_clickable:true
id: 'free' + one_srs.id,
size: one_srs.size - srs_used_size,
textSize: bytesToSizeFilter(one_srs.size - srs_used_size),
type: 'vdi',
non_clickable: true
})
}
root.children.push(srs_storage)
@@ -127,8 +181,6 @@ export default angular.module('dashboard.dataviz', [
root.textSize = bytesToSizeFilter(root.size)
}
let storage_children,
pools,
hostsByPool,
@@ -146,25 +198,25 @@ export default angular.module('dashboard.dataviz', [
name: pool.name_label || 'no pool',
id: pool_id,
children: [],
size:0,
size: 0,
color: !!pool.name_label ? null : 'white',
type:'pool',
type: 'pool',
non_clickable: !pool.name_label
}
pool_shared_storage = {
name: 'Shared',
id: 'Shared'+pool_id,
id: 'Shared' + pool_id,
children: [],
size:0,
type:'host',
non_clickable:true
size: 0,
type: 'host',
non_clickable: true
}
populatestorage(pool_shared_storage,pool_id);
populatestorage(pool_shared_storage, pool_id)
pool_storage.children.push(pool_shared_storage)
pool_storage.size += pool_shared_storage.size
//by hosts
// by hosts
pool_ram = {
name: pool.name_label || 'no pool',

View File

@@ -47,7 +47,31 @@
.grid-cell
.panel.panel-default
.panel-heading.panel-title
weekheatmap(
chart-data="charts.heatmap"
days="['Sun','Mon','Tue','Wed','Thu','Fri']"
)
//- i.xo-icon-memory
| Stat Heatmap
.panel-body.text-center
form
.grid-sm
.grid-cell.grid--gutters
.container-fluid
.form-group
ui-select(ng-model = 'ctrl.selected', ng-change = 'ctrl.prepareMetrics(ctrl.selected.id)')
ui-select-match(placeholder = 'Choose an object')
i(class = 'xo-icon-{{ $select.selected.type | lowercase }}')
| {{ $select.selected.name_label }}
ui-select-choices(repeat = 'object in ctrl.objects | underStat | orderBy:["type", "name_label"]')
div
i(class = 'xo-icon-{{ object.type | lowercase }}')
| {{ object.name_label }}
span(ng-if="(object.type === 'SR' || object.type === 'VM') && object.$container")
| ({{ (object.$container | resolve).name_label }})
.grid-cell.grid--gutters
.container-fluid
span(ng-if = 'ctrl.loadingMetrics')
| Loading metrics ...&nbsp;
i.fa.fa-circle-o-notch.fa-spin
.form-group(ng-if = 'ctrl.metrics')
ui-select(ng-model = 'ctrl.selectedMetric')
ui-select-match(placeholder = 'Choose a metric') {{ $select.selected.key }}
ui-select-choices(repeat = 'metric in ctrl.metrics | orderBy:["key"]') {{ metric.key }}
weekheatmap(ng-if = 'ctrl.selectedMetric', chart-data="ctrl.selectedMetric.values", days="['Sun','Mon','Tue','Wed','Thu','Fri']")

View File

@@ -1,130 +1,127 @@
'use strict'
import angular from 'angular'
import d3 from 'd3'
import clone from 'lodash.clonedeep'
import 'd3'
import 'lodash.clonedeep'
import foreach from 'lodash.foreach'
import pluck from 'lodash.pluck'
angular
.module('xoWebApp.directives')
.directive('weekheatmap', heatmap);
angular
.module('xoWebApp.directives')
.directive('weekheatmap', heatmap)
function heatmap () {
return {
restrict: 'E',
replace: false,
scope: {
chartData: '=',
days: '=',
steps: '=',
colors: '='
},
link,
// @todo : should load template.html instead of this ugly inline string
template: `<div ng-if="matrix.length >0">
<table style="border-spacing:2px;border-collapse:separate;margin-bottom:20px;width:100%">
<tr>
<th></th>
<th ng-repeat="hour in hours" style="text-align:center">
{{hour}}
</th>
</tr>
<tr ng-repeat="(pos,day) in days" style="">
<th style="border:none;">{{day}}</th>
<td
ng-repeat="hour in hours"
tooltip="{{matrix[pos][hour].nb > 1 ? \'avg: \'+matrix[pos][hour].value / matrix[pos][hour].nb: \'val: \'+matrix[pos][hour].value}}"
style=" transition: background-color 300ms linear; width:38px;height:38px;padding:1px;background-color:{{matrix[pos][hour].color}};border-radius:4px">
</td>
</tr>
</table>
<div class="legend">
<div class="pull-left" ng-repeat="interval in intervals" style="width:75px;">
<div style="background-color:{{interval.color}};height:20px"> </div>
<div style="text-align:right;color:gray" > &gt;= {{interval.start}} </div>
</div>
</div>
</div>`
}
function heatmap(){
return {
restrict: 'E',
replace: false,
scope: {
chartData: '=',
days: '=',
steps: '=',
colors: '='
},
link:link,
//@todo : should load template.html instead of this ugly inline string
template:'<div ng-if="matrix.length >0">\
<table style="border-spacing:2px;border-collapse:separate;margin-bottom:20px;width:100%">\
<tr>\
<th></th>\
<th ng-repeat="hour in hours" style="text-align:center">\
{{hour}}\
</th>\
</tr>\
<tr ng-repeat="(pos,day) in days" style="">\
<th style="border:none;">{{day}}</th>\
<td\
ng-repeat="hour in hours" \
title="{{matrix[pos][hour].nb > 1 ? \'val: \'+round(matrix[pos][hour].value) : \'avg: \'+round(matrix[pos][hour].value)}}"\
style=" transition: background-color 300ms linear; width:38px;height:38px;padding:1px;background-color:{{matrix[pos][hour].color}};border-radius:4px">\
</td> \
</tr>\
</table>\
<div class="legend">\
<div class="pull-left" ng-repeat="interval in intervals" style="width:75px;">\
<div style="background-color:{{interval.color}};height:20px"> </div>\
<div style="text-align:right;color:gray" > &gt;= {{interval.start}} </div>\
</div>\
</div>\
</div>',
function link (scope, element, attrs) {
scope.matrix = []
scope.days = scope.days || ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
scope.colors = scope.colors || ['#ffffee', '#ffffd9', '#edf8b1', '#c7e9b4', '#7fcdbb', '#41b6c4', '#1d91c0', '#225ea8', '#253494', '#081d58']
}
scope.hours = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
function link(scope, element, attrs) {
scope.matrix =[];
scope.days = scope.days || ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
scope.round = Math.round;
scope.colors = scope.colors || ["white","#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"]
if (scope.steps) {
scope.intervals = []
scope.hours = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
if(scope.steps){
scope.intervals = [];
for( var i = 1 ; i < scope.steps.length ;i ++){
scope.intervals.push({
start:scope.steps[i-1],
end:scope.steps[i],
color:scope.colors[i]
})
}
}
scope.$watch(function(){return scope.chartData}, function (newVal) {
// console.log(scope.chartData);
if(!scope.chartData){
return;
}
scope.min = Math.min.apply(null,pluck(scope.chartData,'value'))
scope.max = Math.max.apply(null,pluck(scope.chartData,'value'))
if(!scope.steps){
scope.intervals = [];
foreach(scope.colors, function(color,idx){
scope.intervals.push({
start:Math.round(scope.min+idx*(scope.max-scope.min)/scope.colors.length),
end:Math.round(scope.min+(1+idx)*(scope.max-scope.min)/scope.colors.length),
color:color
})
for (var i = 1; i < scope.steps.length; i++) {
scope.intervals.push({
start: scope.steps[i - 1],
end: scope.steps[i],
color: scope.colors[i]
})
}
var matrix=[]
foreach(scope.chartData, function(datum){
var d = new Date(datum.date),
v = datum.value,
day = d.getDay(),
hour = d.getHours();
if(!matrix[day]){
matrix[day] = []
}
if(!matrix[day][hour]){
matrix[day][hour] = {
value:v,
nb:1
}
}else{
matrix[day][hour].value = (v+matrix[day][hour].value)/(matrix[day][hour].nb+1)
matrix[day][hour].nb ++;
}
matrix[day][hour].color = color( matrix[day][hour].value)
})
scope.matrix = matrix;
}
scope.$watch(function () {return scope.chartData}, function (newVal) {
// console.log(scope.chartData);
displayData()
})
function color(value){
var color = null;
foreach(scope.intervals, function(interval,pos){
if(interval.start <= value && interval.end > value){
color = interval.color;
}
function displayData () {
if (!scope.chartData) {
return
}
scope.min = Math.min.apply(null, pluck(scope.chartData, 'value'))
scope.max = Math.max.apply(null, pluck(scope.chartData, 'value'))
if (!scope.steps) {
scope.intervals = []
foreach(scope.colors, function (color, idx) {
scope.intervals.push({
start: scope.min + idx * (scope.max - scope.min) / scope.colors.length,
end: scope.min + (1 + idx) * (scope.max - scope.min) / scope.colors.length,
color: color
})
})
return color;
}
var matrix = []
foreach(scope.chartData, function (datum) {
const d = new Date(datum.date)
const v = datum.value
const day = d.getDay()
const hour = d.getHours()
if (!matrix[day]) {
matrix[day] = []
}
if (!matrix[day][hour]) {
matrix[day][hour] = {
value: v,
nb: 1
}
} else {
matrix[day][hour].value += v
matrix[day][hour].nb++
}
matrix[day][hour].color = color(matrix[day][hour].value / matrix[day][hour].nb)
})
scope.matrix = matrix
}
function color (value) {
var color = null
foreach(scope.intervals, function (interval, pos) {
if (interval.start <= value && value < interval.end || (pos === scope.intervals.length - 1 && value === interval.end)) {
color = interval.color
}
})
return color
}
displayData()
}
}