tree-selector.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. import Ember from 'ember';
  19. const INBETWEEN_HEIGHT = 130;
  20. export default Ember.Component.extend({
  21. // Map: <queue-name, queue>
  22. map : undefined,
  23. // Normalized data for d3
  24. treeData: undefined,
  25. // folded queues, folded[<queue-name>] == true means <queue-name> is folded
  26. foldedQueues: { },
  27. // maxDepth
  28. maxDepth: 0,
  29. // num of leaf queue, folded queue is treated as leaf queue
  30. numOfLeafQueue: 0,
  31. // mainSvg
  32. mainSvg: undefined,
  33. used: undefined,
  34. max: undefined,
  35. // Init data
  36. initData: function() {
  37. this.map = { };
  38. this.treeData = { };
  39. this.maxDepth = 0;
  40. this.numOfLeafQueue = 0;
  41. this.get("model")
  42. .forEach(function(o) {
  43. this.map[o.id] = o;
  44. }.bind(this));
  45. // var selected = this.get("selected");
  46. this.used = this.get("used");
  47. this.max = this.get("max");
  48. this.initQueue("root", 1, this.treeData);
  49. },
  50. // get Children array of given queue
  51. getChildrenNamesArray: function(q) {
  52. var namesArr = [];
  53. // Folded queue's children is empty
  54. if (this.foldedQueues[q.get("name")]) {
  55. return namesArr;
  56. }
  57. var names = q.get("children");
  58. if (names) {
  59. names.forEach(function(name) {
  60. namesArr.push(name);
  61. });
  62. }
  63. return namesArr;
  64. },
  65. // Init queues
  66. initQueue: function(queueName, depth, node) {
  67. if ((!queueName) || (!this.map[queueName])) {
  68. // Queue is not existed
  69. return;
  70. }
  71. if (depth > this.maxDepth) {
  72. this.maxDepth = this.maxDepth + 1;
  73. }
  74. var queue = this.map[queueName];
  75. var names = this.getChildrenNamesArray(queue);
  76. node.name = queueName;
  77. node.parent = queue.get("parent");
  78. node.queueData = queue;
  79. if (names.length > 0) {
  80. node.children = [];
  81. names.forEach(function(name) {
  82. var childQueueData = {};
  83. node.children.push(childQueueData);
  84. this.initQueue(name, depth + 1, childQueueData);
  85. }.bind(this));
  86. } else {
  87. this.numOfLeafQueue = this.numOfLeafQueue + 1;
  88. }
  89. },
  90. update: function(source, root, tree, diagonal) {
  91. var duration = 300;
  92. var i = 0;
  93. // Compute the new tree layout.
  94. var nodes = tree.nodes(root).reverse();
  95. var links = tree.links(nodes);
  96. // Normalize for fixed-depth.
  97. nodes.forEach(function(d) { d.y = d.depth * 200; });
  98. // Update the nodes…
  99. var node = this.mainSvg.selectAll("g.node")
  100. .data(nodes, function(d) { return d.id || (d.id = ++i); });
  101. // Enter any new nodes at the parent's previous position.
  102. var nodeEnter = node.enter().append("g")
  103. .attr("class", "node")
  104. .attr("transform", function() { return "translate(" + source.y0 + "," + source.x0 + ")"; })
  105. .on("click", function(d){
  106. if (d.queueData.get("name") !== this.get("selected")) {
  107. document.location.href = "#/yarn-queues/" + d.queueData.get("name") + "!";
  108. }
  109. Ember.run.later(this, function () {
  110. var treeWidth = this.maxDepth * 200;
  111. var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
  112. var tree = d3.layout.tree().size([treeHeight, treeWidth]);
  113. var diagonal = d3.svg.diagonal()
  114. .projection(function(d) { return [d.y, d.x]; });
  115. this.update(this.treeData, this.treeData, tree, diagonal);
  116. }, 100);
  117. }.bind(this))
  118. .on("dblclick", function (d) {
  119. document.location.href = "#/yarn-queue/" + d.queueData.get("name") + "/info";
  120. });
  121. nodeEnter.append("circle")
  122. .attr("r", 1e-6)
  123. .style("fill", function(d) {
  124. var maxCap = d.queueData.get(this.max);
  125. maxCap = maxCap === undefined ? 100 : maxCap;
  126. var usedCap = d.queueData.get(this.used) / maxCap * 100.0;
  127. if (usedCap <= 60.0) {
  128. return "LimeGreen";
  129. } else if (usedCap <= 100.0) {
  130. return "DarkOrange";
  131. } else {
  132. return "LightCoral";
  133. }
  134. }.bind(this));
  135. // append percentage
  136. nodeEnter.append("text")
  137. .attr("x", function() { return 0; })
  138. .attr("dy", ".35em")
  139. .attr("fill", "white")
  140. .attr("text-anchor", function() { return "middle"; })
  141. .text(function(d) {
  142. var maxCap = d.queueData.get(this.max);
  143. maxCap = maxCap === undefined ? 100 : maxCap;
  144. var usedCap = d.queueData.get(this.used) / maxCap * 100.0;
  145. if (usedCap >= 100.0) {
  146. return usedCap.toFixed(0) + "%";
  147. } else {
  148. return usedCap.toFixed(1) + "%";
  149. }
  150. }.bind(this))
  151. .style("fill-opacity", 1e-6);
  152. // append queue name
  153. nodeEnter.append("text")
  154. .attr("x", "0px")
  155. .attr("dy", "45px")
  156. .attr("text-anchor", "middle")
  157. .text(function(d) { return d.name; })
  158. .style("fill-opacity", 1e-6);
  159. // Transition nodes to their new position.
  160. var nodeUpdate = node.transition()
  161. .duration(duration)
  162. .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
  163. nodeUpdate.select("circle")
  164. .attr("r", 30)
  165. .attr("href",
  166. function(d) {
  167. return "#/yarn-queues/" + d.queueData.get("name");
  168. })
  169. .style("stroke-width", function(d) {
  170. if (d.queueData.get("name") === this.get("selected")) {
  171. return 7;
  172. } else {
  173. return 2;
  174. }
  175. }.bind(this))
  176. .style("stroke", function(d) {
  177. if (d.queueData.get("name") === this.get("selected")) {
  178. return "gray";
  179. } else {
  180. return "gray";
  181. }
  182. }.bind(this));
  183. nodeUpdate.selectAll("text")
  184. .style("fill-opacity", 1);
  185. // Transition exiting nodes to the parent's new position.
  186. var nodeExit = node.exit().transition()
  187. .duration(duration)
  188. .attr("transform", function() { return "translate(" + source.y + "," + source.x + ")"; })
  189. .remove();
  190. nodeExit.select("circle")
  191. .attr("r", 1e-6);
  192. nodeExit.select("text")
  193. .style("fill-opacity", 1e-6);
  194. // Update the links…
  195. var link = this.mainSvg.selectAll("path.link")
  196. .data(links, function(d) { return d.target.id; });
  197. // Enter any new links at the parent's previous position.
  198. link.enter().insert("path", "g")
  199. .attr("class", "link")
  200. .attr("d", function() {
  201. var o = {x: source.x0, y: source.y0};
  202. return diagonal({source: o, target: o});
  203. });
  204. // Transition links to their new position.
  205. link.transition()
  206. .duration(duration)
  207. .attr("d", diagonal);
  208. // Transition exiting nodes to the parent's new position.
  209. link.exit().transition()
  210. .duration(duration)
  211. .attr("d", function() {
  212. var o = {x: source.x, y: source.y};
  213. return diagonal({source: o, target: o});
  214. })
  215. .remove();
  216. // Stash the old positions for transition.
  217. nodes.forEach(function(d) {
  218. d.x0 = d.x;
  219. d.y0 = d.y;
  220. });
  221. },
  222. reDraw: function() {
  223. this.initData();
  224. var margin = {top: 20, right: 120, bottom: 20, left: 120};
  225. var treeWidth = this.maxDepth * 200;
  226. var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
  227. var width = treeWidth + margin.left + margin.right;
  228. var height = treeHeight + margin.top + margin.bottom;
  229. if (this.mainSvg) {
  230. this.mainSvg.remove();
  231. }
  232. this.mainSvg = d3.select("#" + this.get("parentId")).append("svg")
  233. .attr("width", width)
  234. .attr("height", height)
  235. .attr("class", "tree-selector")
  236. .append("g")
  237. .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  238. var tree = d3.layout.tree().size([treeHeight, treeWidth]);
  239. var diagonal = d3.svg.diagonal()
  240. .projection(function(d) { return [d.y, d.x]; });
  241. var root = this.treeData;
  242. root.x0 = height / 2;
  243. root.y0 = 0;
  244. d3.select(window.frameElement).style("height", height);
  245. this.update(root, root, tree, diagonal);
  246. },
  247. didInsertElement: function() {
  248. this.reDraw();
  249. }
  250. });