replace cubism with a custom implementation
This commit is contained in:
@@ -9,6 +9,7 @@ import foreach from 'lodash.foreach'
|
|||||||
|
|
||||||
import xoApi from 'xo-api'
|
import xoApi from 'xo-api'
|
||||||
import xoCubism from'xo-cubism'
|
import xoCubism from'xo-cubism'
|
||||||
|
import xoHorizon from'xo-horizon'
|
||||||
import xoParallelD3 from 'xo-parallel-d3'
|
import xoParallelD3 from 'xo-parallel-d3'
|
||||||
import xoSunburstD3 from 'xo-sunburst-d3'
|
import xoSunburstD3 from 'xo-sunburst-d3'
|
||||||
import xoWeekHeatmap from'xo-week-heatmap'
|
import xoWeekHeatmap from'xo-week-heatmap'
|
||||||
@@ -20,6 +21,7 @@ export default angular.module('dashboard.dataviz', [
|
|||||||
uiSelect,
|
uiSelect,
|
||||||
xoApi,
|
xoApi,
|
||||||
xoCubism,
|
xoCubism,
|
||||||
|
xoHorizon,
|
||||||
xoParallelD3,
|
xoParallelD3,
|
||||||
xoSunburstD3,
|
xoSunburstD3,
|
||||||
xoWeekHeatmap
|
xoWeekHeatmap
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ export default angular.module('dashboard.health', [
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctrl.objects = xoApi.all
|
ctrl.objects = xoApi.all
|
||||||
ctrl.choosen = []
|
ctrl.chosen = []
|
||||||
this.prepareTypeFilter = function (selection) {
|
this.prepareTypeFilter = function (selection) {
|
||||||
const object = selection[0]
|
const object = selection[0]
|
||||||
ctrl.typeFilter = object && object.type || undefined
|
ctrl.typeFilter = object && object.type || undefined
|
||||||
@@ -278,10 +278,10 @@ export default angular.module('dashboard.health', [
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.prepareMetrics = function (objects) {
|
this.prepareMetrics = function (objects) {
|
||||||
ctrl.choosen = objects
|
ctrl.chosen = objects
|
||||||
ctrl.loadingMetrics = true
|
ctrl.loadingMetrics = true
|
||||||
xoAggregate
|
xoAggregate
|
||||||
.refreshStats(ctrl.choosen)
|
.refreshStats(ctrl.chosen, 2)
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
stats = result
|
stats = result
|
||||||
ctrl.metrics = stats.keys
|
ctrl.metrics = stats.keys
|
||||||
@@ -298,8 +298,7 @@ export default angular.module('dashboard.health', [
|
|||||||
console.log('preparestat')
|
console.log('preparestat')
|
||||||
ctrl.stats = {}
|
ctrl.stats = {}
|
||||||
forEach(stats.details, function (stat, object_id) {
|
forEach(stats.details, function (stat, object_id) {
|
||||||
const label = find(ctrl.choosen, {id: object_id})
|
const label = find(ctrl.chosen, {id: object_id})
|
||||||
console.log(label)
|
|
||||||
ctrl.stats[ctrl.selectedMetric + ' ' + label.name_label] = stat[ctrl.selectedMetric]
|
ctrl.stats[ctrl.selectedMetric + ' ' + label.name_label] = stat[ctrl.selectedMetric]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,10 +86,10 @@
|
|||||||
.btn-group(role = 'group')
|
.btn-group(role = 'group')
|
||||||
button.btn.btn-default(ng-click = 'cubism.selected = []', tooltip = 'Clear selection')
|
button.btn.btn-default(ng-click = 'cubism.selected = []', tooltip = 'Clear selection')
|
||||||
i.fa.fa-times
|
i.fa.fa-times
|
||||||
// button.btn.btn-default(ng-click = 'cubism.selectAll("VM")', tooltip = 'Choose all VMs')
|
button.btn.btn-default(ng-click = 'cubism.selectAll("VM")', tooltip = 'Choose all VMs')
|
||||||
// i.xo-icon-vm
|
i.xo-icon-vm
|
||||||
// button.btn.btn-default(ng-click = 'cubism.selectAll("host")', tooltip = 'Choose all hosts')
|
button.btn.btn-default(ng-click = 'cubism.selectAll("host")', tooltip = 'Choose all hosts')
|
||||||
// i.xo-icon-host
|
i.xo-icon-host
|
||||||
button.btn.btn-success(ng-click = 'cubism.prepareMetrics(cubism.selected)', tooltip = 'Load metrics')
|
button.btn.btn-success(ng-click = 'cubism.prepareMetrics(cubism.selected)', tooltip = 'Load metrics')
|
||||||
i.fa.fa-check
|
i.fa.fa-check
|
||||||
| Select
|
| Select
|
||||||
@@ -110,11 +110,30 @@
|
|||||||
span(ng-if = '!object._ignored') {{ object.name_label }}
|
span(ng-if = '!object._ignored') {{ object.name_label }}
|
||||||
del(ng-if = 'object._ignored') {{ object.name_label }}
|
del(ng-if = 'object._ignored') {{ object.name_label }}
|
||||||
|  
|
|  
|
||||||
cubism(
|
//cubism(
|
||||||
ng-if='!cubism.loadingMetrics && cubism.selectedMetric'
|
// ng-if='!cubism.loadingMetrics && cubism.selectedMetric'
|
||||||
chart-data='cubism.stats'
|
// chart-data='cubism.stats'
|
||||||
extents='extents'
|
// extents='extents'
|
||||||
step='1000*5'
|
// step='1000*5'
|
||||||
size='168'
|
// size='168'
|
||||||
|
// )
|
||||||
|
div(ng-repeat="stat in cubism.stats")
|
||||||
|
horizon(
|
||||||
|
ng-if='$first'
|
||||||
|
chart-data='stat'
|
||||||
|
show-axis='true'
|
||||||
|
axis-orientation='top'
|
||||||
|
selected='cubism.selected'
|
||||||
|
)
|
||||||
|
horizon(
|
||||||
|
ng-if='$middle'
|
||||||
|
chart-data='stat'
|
||||||
|
selected='cubism.selected'
|
||||||
|
)
|
||||||
|
horizon(
|
||||||
|
ng-if='$last && !$first'
|
||||||
|
chart-data='stat'
|
||||||
|
show-axis='true'
|
||||||
|
axis-orientation='bottom'
|
||||||
|
selected='cubism.selected'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
import angular from 'angular'
|
||||||
|
import d3 from 'd3'
|
||||||
|
import find from 'lodash.find'
|
||||||
|
import forEach from 'lodash.forEach'
|
||||||
|
console.log('loaded')
|
||||||
|
export default angular.module('xoHorizon', [])
|
||||||
|
.directive('horizon', function ($parse) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
replace: false,
|
||||||
|
scope: {
|
||||||
|
chartData: '=',
|
||||||
|
selected: '='
|
||||||
|
},
|
||||||
|
link: link
|
||||||
|
}
|
||||||
|
function link (scope, element, attrs) {
|
||||||
|
console.log('link')
|
||||||
|
let nbSteps, margin, width, height, color, data
|
||||||
|
|
||||||
|
nbSteps = attrs.steps || 4
|
||||||
|
margin = {
|
||||||
|
top: attrs.showAxis && attrs.axisOrientation !== 'bottom' ? 20 : 0,
|
||||||
|
right: 20,
|
||||||
|
bottom: attrs.showAxis && attrs.axisOrientation === 'bottom' ? 20 : 0,
|
||||||
|
left: 20
|
||||||
|
}
|
||||||
|
width = attrs.width || 960 - margin.left - margin.right
|
||||||
|
height = attrs.height || 70 - margin.top - margin.bottom + (attrs.showAxis ? 20 : 0)
|
||||||
|
color = attrs.color || 'darkgreen'
|
||||||
|
|
||||||
|
const x = d3
|
||||||
|
.time
|
||||||
|
.scale()
|
||||||
|
.range([0, width])
|
||||||
|
|
||||||
|
const y = d3
|
||||||
|
.scale.linear()
|
||||||
|
.range([height, 0])
|
||||||
|
|
||||||
|
const xAxis = d3
|
||||||
|
.svg
|
||||||
|
.axis()
|
||||||
|
.scale(x)
|
||||||
|
.orient(attrs.axisOrientation || 'top')
|
||||||
|
|
||||||
|
const line = d3
|
||||||
|
.svg
|
||||||
|
.line()
|
||||||
|
.x(function (d) {
|
||||||
|
return x(d.date)
|
||||||
|
})
|
||||||
|
.y(function (d) {
|
||||||
|
return y(d.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const bisectDate = d3
|
||||||
|
.bisector(function (d) {
|
||||||
|
return d.date
|
||||||
|
}).left
|
||||||
|
|
||||||
|
const svg = d3
|
||||||
|
.select(element[0])
|
||||||
|
.append('svg')
|
||||||
|
.attr('width', width + margin.left + margin.right)
|
||||||
|
.attr('height', height + margin.top + margin.bottom)
|
||||||
|
.append('g')
|
||||||
|
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
const focus = svg
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'focus')
|
||||||
|
.style('display', 'none')
|
||||||
|
|
||||||
|
focus
|
||||||
|
.append('text')
|
||||||
|
.attr('x', 9)
|
||||||
|
.attr('dy', '.35em')
|
||||||
|
|
||||||
|
svg
|
||||||
|
.append('rect')
|
||||||
|
.attr('class', 'overlay')
|
||||||
|
.attr('width', width)
|
||||||
|
.attr('height', height)
|
||||||
|
.style({fill: 'none', 'pointer-events': 'all'})
|
||||||
|
.on('mousemove', mousemove)
|
||||||
|
|
||||||
|
const hover = svg
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'hover-container')
|
||||||
|
.style('pointer-events', 'none')
|
||||||
|
|
||||||
|
hover
|
||||||
|
.append('line')
|
||||||
|
.attr('y1', 0)
|
||||||
|
.attr('y2', height)
|
||||||
|
.style('stroke-width', 2)
|
||||||
|
.style('stroke-dasharray', '5 5')
|
||||||
|
.style('stroke', 'red')
|
||||||
|
.style('fill', 'none')
|
||||||
|
|
||||||
|
hover
|
||||||
|
.append('text')
|
||||||
|
.style({stroke: 'black'})
|
||||||
|
|
||||||
|
function mousemove () {
|
||||||
|
const x0 = x.invert(d3.mouse(this)[0])
|
||||||
|
const index = bisectDate(data, x0, 1)
|
||||||
|
const d0 = data[index - 1]
|
||||||
|
const d1 = data[index]
|
||||||
|
const d = x0 - d0.date > d1.date - x0 ? d1 : d0
|
||||||
|
scope.$apply(function () {
|
||||||
|
scope.selected = d.date
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function hoverDate (date) {
|
||||||
|
const val = find(data, {date: date})
|
||||||
|
if (val) {
|
||||||
|
hover
|
||||||
|
.select('line')
|
||||||
|
.attr('x1', x(date))
|
||||||
|
.attr('x2', x(date))
|
||||||
|
hover
|
||||||
|
.select('text')
|
||||||
|
.attr('dx', x(date) + 5)
|
||||||
|
.attr('dy', height / 2)
|
||||||
|
.text(val.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d3.selection.prototype.moveToFront = function () {
|
||||||
|
return this.each(function () {
|
||||||
|
this.parentNode.appendChild(this)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.$watch('selected', function (newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
hoverDate(newVal)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
scope.$watch(function () {
|
||||||
|
return scope.chartData
|
||||||
|
}, function (newVal) {
|
||||||
|
let splittedData, counter, extent
|
||||||
|
if (!newVal || !newVal.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data = newVal
|
||||||
|
extent = scope.extent || d3
|
||||||
|
.extent(newVal, function (d) {
|
||||||
|
return d.value
|
||||||
|
})
|
||||||
|
extent = [Math.min(0, extent[0]), Math.max(0, extent[1])]
|
||||||
|
const intervalSize = (extent[1] - extent[0]) / nbSteps
|
||||||
|
|
||||||
|
splittedData = []
|
||||||
|
for (counter = 0; counter < nbSteps; counter++) {
|
||||||
|
splittedData[counter] = []
|
||||||
|
var d = new Date(newVal[0].date)
|
||||||
|
splittedData[counter].push({
|
||||||
|
date: d,
|
||||||
|
value: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
forEach(newVal, function (datum) {
|
||||||
|
for (counter = 0; counter < nbSteps; counter++) {
|
||||||
|
var d = new Date(datum.date)
|
||||||
|
splittedData[counter].push({
|
||||||
|
date: d,
|
||||||
|
value: Math.min(Math.max(0, datum.value - intervalSize * counter), intervalSize),
|
||||||
|
close: new Date(+d + 60 * 60 * 1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
for (counter = 0; counter < nbSteps; counter++) {
|
||||||
|
const d = new Date(newVal[newVal.length - 1].date)
|
||||||
|
splittedData[counter].push({
|
||||||
|
date: d,
|
||||||
|
value: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
x.domain(d3.extent(splittedData[0], function (d) {
|
||||||
|
return d.date
|
||||||
|
}))
|
||||||
|
y.domain([extent[0], extent[1] / nbSteps])
|
||||||
|
if (attrs.showAxis) {
|
||||||
|
var bottomaxis = svg
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'x axis')
|
||||||
|
.style({
|
||||||
|
fill: 'none',
|
||||||
|
stroke: '#000',
|
||||||
|
'shape-rendering': 'crispEdges'
|
||||||
|
})
|
||||||
|
.call(xAxis)
|
||||||
|
if (attrs.axisOrientation === 'bottom') {
|
||||||
|
bottomaxis.attr('transform', 'translate(0,' + height + ')')
|
||||||
|
}
|
||||||
|
bottomaxis.selectAll('text').style({fill: 'black', stroke: 'transparent'})
|
||||||
|
}
|
||||||
|
forEach(splittedData, function (oneSplitted) {
|
||||||
|
svg
|
||||||
|
.append('path')
|
||||||
|
.attr('class', 'horizon-area')
|
||||||
|
.datum(oneSplitted)
|
||||||
|
.style({
|
||||||
|
fill: color,
|
||||||
|
stroke: 'transparent',
|
||||||
|
'fill-opacity': 0.25,
|
||||||
|
'stroke-opacity': 0.3
|
||||||
|
})
|
||||||
|
.attr('d', line)
|
||||||
|
})
|
||||||
|
|
||||||
|
svg.select('.overlay').moveToFront()
|
||||||
|
svg.select('.hover-container').moveToFront()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).name
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"browserify": {
|
||||||
|
"transform": [
|
||||||
|
"babelify"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -34,6 +34,11 @@ export default angular.module('xoWebApp.services', [
|
|||||||
statPromises.push(
|
statPromises.push(
|
||||||
xo[apiType].refreshStats(object.id, level) // 2: week granularity (7 * 24 hours)
|
xo[apiType].refreshStats(object.id, level) // 2: week granularity (7 * 24 hours)
|
||||||
.then(rawStats => ({object, rawStats}))
|
.then(rawStats => ({object, rawStats}))
|
||||||
|
.catch(error => {
|
||||||
|
error.object = object
|
||||||
|
object._ignored = true
|
||||||
|
throw error
|
||||||
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user