mma-server/src/main/resources/com/aliyun/odps/mma/server/ui/static/dag-viz.js
/*
 * Render the RDD DAG visualization on the job page.
 *
 * Due to limitations in dagre-d3, each stage is rendered independently so that
 * we have more control on how to position them. Unfortunately, this means we
 * cannot rely on dagre-d3 to render edges that cross stages and must render
 * these manually on our own.
 */
Render the RDD DAG visualization on the job page.
Due to limitations in dagre-d3, each stage is rendered independently so that
we have more control on how to position them. Unfortunately, this means we
cannot rely on dagre-d3 to render edges that cross stages and must render
these manually on our own.
Render the RDD DAG visualization on the job page . Due to limitations in dagre - d3 each stage is rendered independently so that we have more control on how to position them . Unfortunately this means we cannot rely on dagre - d3 to render edges that cross stages and must render these manually on our own .
function renderDagVizForJob(svgContainer) {
  var crossStageEdges = [];
  metadataContainer().selectAll(".stage-metadata").each(function(d, i) {
    var metadata = d3.select(this);
    var dot = metadata.select(".dot-file").text();
    var stageId = metadata.attr("stage-id");
    var containerId = VizConstants.graphPrefix + stageId;
    var isSkipped = metadata.attr("skipped") == "true";
    var container;
    if (isSkipped) {
      container = svgContainer
      .append("g")
      .attr("id", containerId)
      .attr("skipped", "true");
    } else {
      var attemptId = 0
      var stageLink = d3.select("#stage-" + stageId + "-" + attemptId)
      .select("a.name-link")
      .attr("href");
      container = svgContainer
      .append("a")
      .attr("xlink:href", stageLink)
      .attr("onclick", "window.localStorage.setItem(expandDagVizArrowKey(false), true)")
      .append("g")
      .attr("id", containerId);
    }
    if (i > 0) {
      var existingStages = svgContainer.selectAll("g.cluster.stage")
      if (!existingStages.empty()) {
        var lastStage = d3.select(existingStages[0].pop());
        var lastStageWidth = toFloat(lastStage.select("rect").attr("width"));
        var lastStagePosition = getAbsolutePosition(lastStage);
        var offset = lastStagePosition.x + lastStageWidth + VizConstants.stageSep;
        container.attr("transform", "translate(" + offset + ", 0)");
      }
    }
    renderDot(dot, container, true);
    if (isSkipped) {
      container.selectAll("g").classed("skipped", true);
    }
    container
    .selectAll("rect")
    .attr("rx", "4")
    .attr("ry", "4");
    metadata.selectAll(".incoming-edge").each(function(v) {
      var edge = d3.select(this).text().trim().split(","); 
      crossStageEdges.push(edge);
    });
  });
  addTooltipsForRDDs(svgContainer);
  drawCrossStageEdges(crossStageEdges, svgContainer);
}
function renderDagVizForJob ( svgContainer ) { var crossStageEdges = [ ] ; metadataContainer ( ) . selectAll ( ".stage-metadata" ) . each ( function ( d , i ) { var metadata = d3 . select ( this ) ; var dot = metadata . select ( ".dot-file" ) . text ( ) ; var stageId = metadata . attr ( "stage-id" ) ; var containerId = VizConstants . graphPrefix + stageId ; var isSkipped = metadata . attr ( "skipped" ) == "true" ; var container ; if ( isSkipped ) { container = svgContainer . append ( "g" ) . attr ( "id" , containerId ) . attr ( "skipped" , "true" ) ; } else { var attemptId = 0 var stageLink = d3 . select ( "#stage-" + stageId + "-" + attemptId ) . select ( "a.name-link" ) . attr ( "href" ) ; container = svgContainer . append ( "a" ) . attr ( "xlink:href" , stageLink ) . attr ( "onclick" , "window.localStorage.setItem(expandDagVizArrowKey(false), true)" ) . append ( "g" ) . attr ( "id" , containerId ) ; } if ( i > 0 ) { var existingStages = svgContainer . selectAll ( "g.cluster.stage" ) if ( ! existingStages . empty ( ) ) { var lastStage = d3 . select ( existingStages [ 0 ] . pop ( ) ) ; var lastStageWidth = toFloat ( lastStage . select ( "rect" ) . attr ( "width" ) ) ; var lastStagePosition = getAbsolutePosition ( lastStage ) ; var offset = lastStagePosition . x + lastStageWidth + VizConstants . stageSep ; container . attr ( "transform" , "translate(" + offset + ", 0)" ) ; } } renderDot ( dot , container , true ) ; if ( isSkipped ) { container . selectAll ( "g" ) . classed ( "skipped" , true ) ; } container . selectAll ( "rect" ) . attr ( "rx" , "4" ) . attr ( "ry" , "4" ) ; metadata . selectAll ( ".incoming-edge" ) . each ( function ( v ) { var edge = d3 . select ( this ) . text ( ) . trim ( ) . split ( "," ) ; crossStageEdges . push ( edge ) ; } ) ; } ) ; addTooltipsForRDDs ( svgContainer ) ; drawCrossStageEdges ( crossStageEdges , svgContainer ) ; }
Render the RDD DAG visualization on the job page.
Render the RDD DAG visualization on the job page .
// Each div.stage-metadata contains the information needed to generate the graph // for a stage. This includes the DOT file produced from the appropriate UI listener, // any incoming and outgoing edges, and any cached RDDs that belong to this stage. // Link each graph to the corresponding stage page (TODO: handle stage attempts) // Use the link from the stage table so it also works for the history server // Now we need to shift the container for this stage so it doesn't overlap with // existing ones, taking into account the position and width of the last stage's // container. We do not need to do this for the first stage of this job. // Actually render the stage // Mark elements as skipped if appropriate. Unfortunately we need to mark all // elements instead of the parent container because of CSS override rules. // Round corners on rectangles // If there are any incoming edges into this graph, keep track of them to render // them separately later. Note that we cannot draw them now because we need to // put these edges in a separate container that is on top of all stage graphs. // e.g. 3,4 => [3, 4]