Entity graph hightlight and pin-all

Change-Id: Ia9e653c221243cd0d28cfc1e9ae4c5d6718a6766
This commit is contained in:
Guy Aharonovsky 2016-10-14 00:10:02 +03:00
parent ef9a39e23b
commit 9eac15598f
8 changed files with 239 additions and 31 deletions

View File

@ -7,6 +7,7 @@
</div>
<div class="panel-body">
<hz-entities-info item="vm.selectedItem"></hz-entities-info>
<hz-entities-toolbox></hz-entities-toolbox>
<hz-entities-graph data="vm.graphData" selected="vm.model.selected" item-selected="vm.setSelected"></hz-entities-graph>
</div>
</div>

View File

@ -1,5 +1,5 @@
.entities {
.panel-body {
padding: 3px;
padding: 6px;
}
}

View File

@ -24,14 +24,34 @@ function hzEntitiesGraph() {
linkWidth = 1,
circleRadius = 14,
circlePadding = 1,
pinned = horizon.cookies.get('pinned') || [],
zoom = d3.behavior.zoom().scaleExtent([minZoom, maxZoom]),
ellipsisWidth = 80,
hightlightDepth = 2,
heightOffset,
pinned,
graphCreated,
node,
link,
linksMap,
content;
(function() {
var p = $('.panel.panel-primary');
heightOffset = (p.length ? p.offset().top : 180) + 75;
pinned = horizon.cookies.get('pinned') || [];
if (_.isString(pinned)) {
try {
pinned = JSON.parse(pinned);
}
catch(ex) {
pinned = [];
console.error('Failed to parse the pinned cookie');
}
}
})();
scope.$watch('data.ts', function(newVal, oldVal) {
if (newVal) {
prepareData();
@ -44,6 +64,22 @@ function hzEntitiesGraph() {
}
});
scope.$on('toolbox-pin', function () {
pinAll();
});
scope.$on('toolbox-unpin', function () {
unpinAll();
})
scope.$on('toolbox-zoom-to-fit', function () {
console.log('on toolbox-pin', arguments)
});
scope.$on('toolbox-toggle-fullscreen', function () {
console.log('on toolbox-unpin', arguments)
})
scope.isEmpty = function() {
return scope.data && scope.data.nodes && scope.data.nodes.length === 0;
};
@ -90,7 +126,7 @@ function hzEntitiesGraph() {
d3.select(window).on('resize', resize);
function resize() {
svg.attr('height', window.innerHeight - 168 + 'px')
svg.attr('height', window.innerHeight - heightOffset + 'px')
force.size([angular.element(svg[0]).width(),
angular.element(svg[0]).height()])
.resume();
@ -108,6 +144,11 @@ function hzEntitiesGraph() {
node.y = pin.y;
}
})
linksMap = {};
_.each(scope.data.links, function(link) {
linksMap[link.source.id + ',' + link.target.id] = true;
});
}
function createGraph() {
@ -228,6 +269,7 @@ function hzEntitiesGraph() {
}
window.drawGraph = drawGraph;
window.dforce = force;
function drawGraph() {
link = link.data(force.links(), function(d) { return d.source.id + '-' + d.target.id; });
@ -275,28 +317,47 @@ function hzEntitiesGraph() {
.attr('dominant-baseline', 'central')
.attr('transform', 'scale(1)')
.attr('class', function(d) {
var cls = '';
var severity = d.operational_severity;
if (severity) {
switch (severity.toLowerCase()) {
case 'critical':
cls = 'red';
break;
case 'severe':
cls = 'orange';
break;
case 'warning':
cls = 'yellow';
break;
case 'ok':
cls = 'green';
break;
case 'n/a':
cls = 'gray';
break;
default: //'DISABLED', 'UNKNOWN', 'UNDEFINED'
cls = 'gray';
break;
var category = d.category,
cls = '';
if (category && category.toLowerCase() === 'alarm') {
var severity = d.operational_severity;
if (severity) {
switch (severity.toLowerCase()) {
case 'critical':
cls = 'red';
break;
case 'severe':
cls = 'orange';
break;
case 'warning':
cls = 'yellow';
break;
case 'ok':
cls = 'green';
break;
case 'n/a':
cls = 'gray';
break;
default: //'DISABLED', 'UNKNOWN', 'UNDEFINED'
cls = 'gray';
break;
}
}
} else {
var state = d.operational_state;
if (state) {
switch (state.toLowerCase()) {
case 'error':
cls = 'red';
break;
case 'suboptimal':
cls = 'yellow';
break;
case 'n/a':
cls = 'gray';
break;
}
}
}
return cls;
@ -440,15 +501,48 @@ function hzEntitiesGraph() {
if ($(this).is('.node')) {
d3.select(this).classed('selected', true);
//d3.select(this).classed('selected', true);
findHighlight(d);
}
}
function findHighlight(rootNode) {
_.each(scope.data.nodes, function(node) {
node.high = false;
})
var depth = hightlightDepth;
findNodes(rootNode, depth, scope.data.nodes, linksMap);
_.each(scope.data.links, function(link) {
link.high = false;
})
svg_g.selectAll('.node')
.classed('selected', function(d) {
return d.high;
})
.select('circle')
.style('stroke-width', function(d) {
return d.high ? (Math.max(d.highDepth + 1, 1) * 2) : null;
})
svg_g.selectAll('.link').classed('selected', function(d) {
return d.source.high && d.target.high;
})
}
function selectNone(d) {
nodeClick(null);
}
function pinNode(d) {
d3.event.stopImmediatePropagation();
d3.event.preventDefault();
var node;
if ($(this).is('.node')) {
@ -463,9 +557,6 @@ function hzEntitiesGraph() {
updatePinnedCookie(d);
}
d3.event.stopImmediatePropagation();
d3.event.preventDefault();
//fixing some bug with unpinning
/*setTimeout(function() {
force.resume()
@ -476,7 +567,7 @@ function hzEntitiesGraph() {
var pinIndex = -1;
pinned.forEach(function(pin, i) {
if (pin.id === d.id) {
pinIndex = i
pinIndex = i;
}
})
@ -488,7 +579,7 @@ function hzEntitiesGraph() {
pinned.push({id: d.id, x: d.x, y: d.y});
}
horizon.cookies.put('pinned', pinned);
horizon.cookies.put('pinned', JSON.stringify(pinned));
}
function nodeDragend(d) {
@ -497,6 +588,39 @@ function hzEntitiesGraph() {
}
}
function pinAll() {
pinned = [];
svg_g.selectAll('.node')
.classed('pinned', true)
.each(function(d) {
d.fixed = true;
pinned.push({id: d.id, x: d.x, y: d.y});
})
horizon.cookies.put('pinned', JSON.stringify(pinned));
}
function unpinAll() {
pinned = [];
svg_g.selectAll('.node')
.classed('pinned', false)
.each(function(d) {
d.fixed = false;
})
horizon.cookies.put('pinned', JSON.stringify([]));
setTimeout(function() {
force.resume()
}, 100)
}
function pinAllNodes(isPin) {
}
function setEllipsis(el, text, width) {
el.textContent = text;
@ -516,6 +640,30 @@ function hzEntitiesGraph() {
}
};
function findNodes(rootNode, depth, allNodes, linksMap) {
if (rootNode) {
rootNode.high = true;
rootNode.highDepth = depth;
depth--;
_.each(allNodes, function(node) {
if (linksMap[node.id + ',' + rootNode.id] || linksMap[rootNode.id + ',' + node.id]) {
if (depth > -1 && !node.high) {
findNodes(node, depth, allNodes, linksMap);
} else if (depth <= -1) {
//Always find 'depth' + alarms & (sdns + alarms)
if (node.category.toLowerCase() === 'alarm') {
node.high = true;
node.highDepth = 0;
} else if (!node.high && node.type && node.type.toLowerCase() === 'sdn_controller') {
findNodes(node, depth, allNodes, linksMap);
}
}
}
});
}
}
/*function nodeDragstart(d) {
d3.select(this).classed('pinned', d.fixed = true);

View File

@ -27,6 +27,11 @@ $dark_gray: darkgray;
.link {
stroke-opacity: 0.8;
&.selected {
stroke: $blue;
stroke-width: 2 !important;
}
}
.node {

View File

@ -0,0 +1,24 @@
angular
.module('horizon.dashboard.project.vitrage')
.directive('hzEntitiesToolbox', hzEntitiesToolbox);
hzEntitiesToolbox.$inject = ['$rootScope'];
function hzEntitiesToolbox($rootScope) {
var directive = {
link: link,
templateUrl: STATIC_URL + 'dashboard/project/entities/toolbox/entities-toolbox.html',
restrict: 'E',
scope: {
item: '='
}
};
return directive;
function link(scope, element, attrs) {
scope.broadcast = function(event) {
console.log('click', event);
$rootScope.$broadcast('toolbox-' + event);
}
}
}

View File

@ -0,0 +1,12 @@
<div class="entities-toolbox">
<div class="btn-group btn-group-xs" role="group">
<a href="#" class="btn btn-default" ng-click="broadcast('pin')"><span class="fa fa-thumb-tack"></span> Pin</a>
<a href="#" class="btn btn-default" ng-click="broadcast('unpin')">Unpin</a>
</div>
<!--<div class="btn-group btn-group-xs" role="group">
<a href="#" class="btn btn-default" ng-click="broadcast('zoom-to-fit')"><span class="fa fa-expand"></span> Zoom to fit</a>
</div>
<div class="btn-group btn-group-xs" role="group">
<a href="#" class="btn btn-default" ng-click="broadcast('toggle-fullscreen')"><span class="fa fa-square-o"></span> Toggle fullscreen</a>
</div>-->
</div>

View File

@ -0,0 +1,17 @@
.entities-toolbox {
position: absolute;
right: 18px;
margin: 12px;
padding:8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
background: rgba(255, 255, 255, 0.8);
.btn-group {
margin-right: 6px;
&:last-child {
margin-right: 0;
}
}
}

View File

@ -10,6 +10,7 @@
@import 'components/information/information';
@import 'entities/graph/entities-graph.scss';
@import 'entities/info/entities-info.scss';
@import 'entities/toolbox/entities-toolbox.scss';
@import 'entities/entities.scss';
.red {