You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@falcon.apache.org by ve...@apache.org on 2014/03/25 02:49:41 UTC

[3/4] FALCON-367 Bump dagre and jquery version for the web UI. Contributed by Haohui Mai FALCON-290 Visualize lineage information on the dashboard. Contributed by Haohui Mai FALCON-371 Show vertex information in the web UI. Contributed by Haohui Mai FALC

http://git-wip-us.apache.org/repos/asf/incubator-falcon/blob/24142a1f/html5-ui/js/dust-helpers-1.2.0.min.js
----------------------------------------------------------------------
diff --git a/html5-ui/js/dust-helpers-1.2.0.min.js b/html5-ui/js/dust-helpers-1.2.0.min.js
new file mode 100644
index 0000000..393f75d
--- /dev/null
+++ b/html5-ui/js/dust-helpers-1.2.0.min.js
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+!function(dust){function isSelect(a){var b=a.current();return"object"==typeof b&&b.isSelect===!0}function jsonFilter(a,b){return"function"==typeof b?b.toString().replace(/(^\s+|\s+$)/gm,"").replace(/\n/gm,"").replace(/,\s*/gm,", ").replace(/\)\{/gm,") {"):b}function filter(a,b,c,d,e){d=d||{};var f,g,h=c.block,i=d.filterOpType||"";if("undefined"!=typeof d.key)f=dust.helpers.tap(d.key,a,b);else{if(!isSelect(b))return _console.log("No key specified for filter in:"+i+" helper "),a;f=b.current().selectKey,b.current().isResolved&&(e=function(){return!1})}return g=dust.helpers.tap(d.value,a,b),e(coerce(g,d.type,b),coerce(f,d.type,b))?(isSelect(b)&&(b.current().isResolved=!0),h?a.render(h,b):(_console.log("Missing body block in the "+i+" helper "),a)):c["else"]?a.render(c["else"],b):a}function coerce(a,b,c){if(a)switch(b||typeof a){case"number":return+a;case"string":return String(a);case"boolean":return a="false"===a?!1:a,Boolean(a);case"date":return new Date(a);case"context":return c.get(a
 )}return a}var _console="undefined"!=typeof console?console:{log:function(){}},helpers={tap:function(a,b,c){if("function"!=typeof a)return a;var d,e="";return d=b.tap(function(a){return e+=a,""}).render(a,c),b.untap(),d.constructor!==b.constructor?d:""===e?!1:e},sep:function(a,b,c){var d=c.block;return b.stack.index===b.stack.of-1?a:d?c.block(a,b):a},idx:function(a,b,c){var d=c.block;return d?c.block(a,b.push(b.stack.index)):a},contextDump:function(a,b,c,d){var e,f=d||{},g=f.to||"output",h=f.key||"current";return g=dust.helpers.tap(g,a,b),h=dust.helpers.tap(h,a,b),e="full"===h?JSON.stringify(b.stack,jsonFilter,2):JSON.stringify(b.stack.head,jsonFilter,2),"console"===g?(_console.log(e),a):a.write(e)},"if":function(chunk,context,bodies,params){var body=bodies.block,skip=bodies["else"];if(params&&params.cond){var cond=params.cond;if(cond=dust.helpers.tap(cond,chunk,context),eval(cond))return body?chunk.render(bodies.block,context):(_console.log("Missing body block in the if helper!"),c
 hunk);if(skip)return chunk.render(bodies["else"],context)}else _console.log("No condition given in the if helper!");return chunk},math:function(a,b,c,d){if(d&&"undefined"!=typeof d.key&&d.method){var e=d.key,f=d.method,g=d.operand,h=d.round,i=null;switch(e=dust.helpers.tap(e,a,b),g=dust.helpers.tap(g,a,b),f){case"mod":(0===g||g===-0)&&_console.log("operand for divide operation is 0/-0: expect Nan!"),i=parseFloat(e)%parseFloat(g);break;case"add":i=parseFloat(e)+parseFloat(g);break;case"subtract":i=parseFloat(e)-parseFloat(g);break;case"multiply":i=parseFloat(e)*parseFloat(g);break;case"divide":(0===g||g===-0)&&_console.log("operand for divide operation is 0/-0: expect Nan/Infinity!"),i=parseFloat(e)/parseFloat(g);break;case"ceil":i=Math.ceil(parseFloat(e));break;case"floor":i=Math.floor(parseFloat(e));break;case"round":i=Math.round(parseFloat(e));break;case"abs":i=Math.abs(parseFloat(e));break;default:_console.log("method passed is not supported")}return null!==i?(h&&(i=Math.round(i)
 ),c&&c.block?a.render(c.block,b.push({isSelect:!0,isResolved:!1,selectKey:i})):a.write(i)):a}return _console.log("Key is a required parameter for math helper along with method/operand!"),a},select:function(a,b,c,d){var e=c.block;if(d&&"undefined"!=typeof d.key){var f=dust.helpers.tap(d.key,a,b);return e?a.render(c.block,b.push({isSelect:!0,isResolved:!1,selectKey:f})):(_console.log("Missing body block in the select helper "),a)}return _console.log("No key given in the select helper!"),a},eq:function(a,b,c,d){return d&&(d.filterOpType="eq"),filter(a,b,c,d,function(a,b){return b===a})},ne:function(a,b,c,d){return d?(d.filterOpType="ne",filter(a,b,c,d,function(a,b){return b!==a})):a},lt:function(a,b,c,d){return d?(d.filterOpType="lt",filter(a,b,c,d,function(a,b){return a>b})):void 0},lte:function(a,b,c,d){return d?(d.filterOpType="lte",filter(a,b,c,d,function(a,b){return a>=b})):a},gt:function(a,b,c,d){return d?(d.filterOpType="gt",filter(a,b,c,d,function(a,b){return b>a})):a},gte:func
 tion(a,b,c,d){return d?(d.filterOpType="gte",filter(a,b,c,d,function(a,b){return b>=a})):a},"default":function(a,b,c,d){return d&&(d.filterOpType="default"),filter(a,b,c,d,function(){return!0})},size:function(a,b,c,d){var e,f,g,h=0;if(d=d||{},e=d.key,e&&e!==!0)if(dust.isArray(e))h=e.length;else if(!isNaN(parseFloat(e))&&isFinite(e))h=e;else if("object"==typeof e){f=0;for(g in e)Object.hasOwnProperty.call(e,g)&&f++;h=f}else h=(e+"").length;else h=0;return a.write(h)}};dust.helpers=helpers}("undefined"!=typeof exports?module.exports=require("dustjs-linkedin"):dust);

http://git-wip-us.apache.org/repos/asf/incubator-falcon/blob/24142a1f/html5-ui/js/falcon-entity.js
----------------------------------------------------------------------
diff --git a/html5-ui/js/falcon-entity.js b/html5-ui/js/falcon-entity.js
index 8ae1acb..f0407c4 100644
--- a/html5-ui/js/falcon-entity.js
+++ b/html5-ui/js/falcon-entity.js
@@ -54,6 +54,9 @@
 
       dust.render('instance', data, function(err, out) {
         $('#panel-instance > .panel-body').html(out);
+        $('.lineage-href').click(function() {
+          falcon.load_lineage_graph(entityId, $(this).attr('data-instance-name'));
+        });
         $('.instance-hdfs-log').tooltip();
         $('#panel-instance').show();
       });
@@ -234,10 +237,10 @@
       layout.eachEdge(drawEdge);
       layout.eachNode(drawNode);
 
-      var bb = layout.graph().bbox;
+      var graph = layout.graph();
 
-      $('#entity-dep-graph').attr('width', bb.width);
-      $('#entity-dep-graph').attr('height', bb.height);
+      $('#entity-dep-graph').attr('width', graph.width);
+      $('#entity-dep-graph').attr('height', graph.height);
       postRender();
     }
     plot();

http://git-wip-us.apache.org/repos/asf/incubator-falcon/blob/24142a1f/html5-ui/js/falcon-lineage.js
----------------------------------------------------------------------
diff --git a/html5-ui/js/falcon-lineage.js b/html5-ui/js/falcon-lineage.js
new file mode 100644
index 0000000..b7e0bda
--- /dev/null
+++ b/html5-ui/js/falcon-lineage.js
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function(falcon) {
+  "use strict";
+
+  dust.loadSource(dust.compile($('#lineage-info-tmpl').html(), 'info'));
+  var CIRCLE_RADIUS = 12, RANK_SEPARATION = 120, LABEL_WIDTH = 120, LABEL_HEIGHT = 80, LABEL_PADDING = 20;
+  var PREFIX = '/api/graphs/lineage';
+
+  var data = {
+    queue : {},
+    nodes : {},
+    edges : {},
+    active_node_id : null
+  };
+
+  function process_queue(done_cb) {
+    var q = data.queue;
+    if (q.length == 0) {
+      done_cb();
+      return;
+    }
+
+    function filter_node(n) {
+      return n.type === 'process-instance' || n.type === 'feed-instance';
+    }
+
+    function is_nonterminal_label(e) {
+      return e._label === 'input' || e._label === 'output';
+    }
+
+    function visit_neighbor(p) {
+      var vid = p.id, depth = p.depth;
+      if (depth == 0) {
+        process_queue(done_cb);
+        return;
+      }
+      falcon.getJson(PREFIX + '/vertices/' + vid + '/both', function(resp) {
+        for (var i = 0; i < resp.results.length; ++i) {
+          var n = resp.results[i];
+          if (data.nodes[n._id] !== undefined || !filter_node(n)) {
+            continue;
+          }
+          data.nodes[n._id] = n;
+          q.push({'id': n._id, 'depth': depth - 1});
+        }
+      }).always(function() {
+        process_queue(done_cb);
+      });
+    }
+
+    var v = data.queue.pop();
+    falcon.getJson(PREFIX + '/vertices/' + v.id + '/bothE', function(resp) {
+      var terminal_has_in_edge = false, terminal_has_out_edge = false;
+      var node = data.nodes[v.id];
+      for (var i = 0; i < resp.results.length; ++i) {
+        var e = resp.results[i];
+        if (is_nonterminal_label(e)) {
+          terminal_has_in_edge = terminal_has_in_edge || e._inV == v.id;
+          terminal_has_out_edge = terminal_has_out_edge || e._outV == v.id;
+        }
+        if (data.edges[e._id] !== undefined) {
+          continue;
+        }
+        data.edges[e._id] = e;
+      }
+      node.is_terminal = !(terminal_has_in_edge && terminal_has_out_edge);
+    }).always(function () {
+      visit_neighbor(v);
+    });
+  }
+
+  function draw_graph() {
+    var svg = d3.select('#lineage-graph').html('').append('svg:g');
+
+    var LINE_FUNCTION = d3.svg.line()
+      .x(function(d) { return d.x; })
+      .y(function(d) { return d.y; })
+      .interpolate('basis');
+
+    function draw_node_functor(node_map) {
+      function expand_node(d) {
+        return function() {
+          data.queue.push({'id': d._id, 'depth': 1});
+          update_graph();
+        };
+      }
+
+      function show_info_functor(d) {
+        return function() {
+          svg.selectAll('.lineage-node').classed('lineage-node-active', false);
+          d3.select(this).classed('lineage-node-active', true);
+          data.active_node_id = d._id;
+          falcon.getJson(PREFIX + '/vertices/properties/' + d._id + '?relationships=true', function(resp) {
+            dust.render('info', resp, function(err, out) {
+              $('#lineage-info-panel').html(out);
+            });
+          });
+        };
+      }
+
+      return function(id, d) {
+        var data = node_map[id];
+        var r = svg.append('g')
+          .attr('transform', 'translate(' + d.x + ',' + d.y + ')');
+
+        var c = r.append('circle')
+          .attr('data-node-id', id)
+          .attr('class', 'lineage-node lineage-node-' + data.type)
+          .attr('r', CIRCLE_RADIUS)
+          .on('click', show_info_functor(data));
+
+        if (!data.is_terminal) {
+          c.on('dblclick', expand_node(data));
+        } else {
+          c.classed('lineage-node-terminal', true);
+        }
+
+        var name = node_map[id].name;
+        r.append('title').text(name);
+
+        var fo = r.append('foreignObject')
+          .attr('transform', 'translate(' + (-LABEL_WIDTH / 2) + ', ' + LABEL_PADDING + ' )')
+          .attr('width', LABEL_WIDTH)
+          .attr('height', LABEL_HEIGHT);
+
+        fo.append('xhtml:div').text(name)
+          .attr('class', 'lineage-node-text');
+      };
+    }
+
+    function draw_edge(layout) {
+      return function(e, u, v, value) {
+        var r = svg.append('g').attr('class', 'lineage-link');
+        r.append('path')
+          .attr('marker-end', 'url(#arrowhead)')
+          .attr('d', function() {
+            var points = value.points;
+
+            var source = layout.node(u);
+            var target = layout.node(v);
+
+            var p = points.length === 0 ? source : points[points.length - 1];
+
+            var r = CIRCLE_RADIUS;
+            var sx = p.x, sy = p.y, tx = target.x, ty = target.y;
+            var l = Math.sqrt((tx - sx) * (tx - sx) + (ty - sy) * (ty - sy));
+            var dx = r / l * (tx - sx), dy = r / l * (ty - sy);
+
+            points.unshift({'x': source.x, 'y': source.y});
+            points.push({'x': target.x - dx, 'y': target.y - dy});
+            return LINE_FUNCTION(points);
+        });
+      };
+    }
+
+    var node_map = {};
+    var g = new dagre.Digraph();
+    for (var k in data.nodes) {
+      var n = data.nodes[k];
+      g.addNode(n._id, { 'width': CIRCLE_RADIUS * 2 + LABEL_WIDTH, 'height': CIRCLE_RADIUS * 2 + LABEL_HEIGHT});
+      node_map[n._id] = n;
+    }
+
+    for (var k in data.edges) {
+      var e = data.edges[k];
+      var src = node_map[e._inV], dst = node_map[e._outV];
+      if (src !== undefined && dst !== undefined) {
+        g.addEdge(null, e._outV, e._inV);
+      }
+    }
+
+    var layout = dagre.layout().rankSep(RANK_SEPARATION).rankDir('LR').run(g);
+    layout.eachEdge(draw_edge(layout));
+    layout.eachNode(draw_node_functor(node_map));
+
+    function post_render() {
+      svg
+        .append('svg:defs')
+        .append('svg:marker')
+        .attr('id', 'arrowhead')
+        .attr('viewBox', '0 0 10 10')
+        .attr('refX', 8)
+        .attr('refY', 5)
+        .attr('markerUnits', 'strokeWidth')
+        .attr('markerWidth', 8)
+        .attr('markerHeight', 5)
+        .attr('orient', 'auto')
+        .attr('style', 'fill: #ccc')
+        .append('svg:path')
+        .attr('d', 'M 0 0 L 10 5 L 0 10 z');
+    }
+
+    var bb = layout.graph();
+    $('#lineage-graph').attr('width', bb.width);
+    $('#lineage-graph').attr('height', bb.height);
+
+    post_render();
+  }
+
+  function update_graph() {
+    process_queue(function() {
+      draw_graph();
+      var id = data.active_node_id;
+      if (id !== null) {
+        d3.select('.node[data-node-id="' + id + '"]').classed('node-active', true);
+      }
+    });
+  }
+
+  falcon.load_lineage_graph = function(entityId, instance_name) {
+    var node_name = entityId + '/' + instance_name;
+    falcon.getJson(PREFIX + '/vertices?key=name&value=' + node_name, function(resp) {
+      var n = resp.results[0];
+      data.queue = [{'id': n._id, 'depth': 1}];
+      data.nodes = {};
+      data.nodes[n._id] = n;
+      update_graph();
+      $('#lineage-modal').modal('show');
+    });
+  };
+})(falcon);