Skip to content

Commit

Permalink
Move styling from JS to CSS + clean up code
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Or committed May 6, 2015
1 parent 107c0b6 commit 9dab5f0
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 118 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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.
*/

#dag-viz-graph svg path {
stroke: #444444;
stroke-width: 1.5px;
}

#dag-viz-graph svg g.cluster rect {
stroke-width: 4px;
stroke-opacity: 0.5;
}

#dag-viz-graph svg g.node circle,
#dag-viz-graph svg g.node rect {
fill: #444444;
}

#dag-viz-graph svg g.node.cached circle,
#dag-viz-graph svg g.node.cached rect {
fill: #FF0000;
}

/* Job page specific styles */

#dag-viz-graph svg.job marker#marker-arrow path {
fill: #444444;
stroke-width: 0px;
}

#dag-viz-graph svg.job g.cluster rect {
fill: #FFFFFF;
stroke: #AADFFF;
}

#dag-viz-graph svg.job g.cluster[id*="stage"] rect {
stroke: #FFDDEE;
stroke-width: 6px;
}

#dag-viz-graph svg.job g#cross-stage-edges path {
fill: none;
}

#dag-viz-graph svg.job g.cluster text {
fill: #AAAAAA;
font-size: 11px;
}

/* Stage page specific styles */

#dag-viz-graph svg.stage g.cluster rect {
fill: #F0F8FF;
stroke: #AADFFF;
}

#dag-viz-graph svg.stage g.cluster[id*="stage"] rect {
fill: #FFFFFF;
stroke: #FFDDEE;
stroke-width: 6px;
}

#dag-viz-graph svg.stage g.node g.label text tspan {
fill: #FFFFFF;
}

#dag-viz-graph svg.stage g.cluster text {
fill: #444444;
font-size: 14px;
font-weight: bold;
}
149 changes: 38 additions & 111 deletions core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,6 @@
*/

var VizConstants = {
rddColor: "#444444",
rddCachedColor: "#FF0000",
rddOperationColor: "#AADFFF",
stageColor: "#FFDDEE",
clusterLabelColor: "#888888",
edgeColor: "#444444",
edgeWidth: "1.5px",
svgMarginX: 20,
svgMarginY: 20,
stageSep: 50,
Expand Down Expand Up @@ -113,17 +106,21 @@ function toggleDagViz(forJob) {
function renderDagViz(forJob) {

// If there is not a dot file to render, fail fast and report error
var jobOrStage = forJob ? "job" : "stage";
if (metadataContainer().empty()) {
graphContainer().append("div").text(
"No visualization information available for this " + (forJob ? "job" : "stage"));
"No visualization information available for this " + jobOrStage);
return;
}

var svg = graphContainer().append("svg");
var svg = graphContainer().append("svg").attr("class", jobOrStage);

if (forJob) {
renderDagVizForJob(svg);
postProcessDagVizForJob();
} else {
renderDagVizForStage(svg);
postProcessDagVizForStage();
}

// Find cached RDDs
Expand All @@ -137,9 +134,17 @@ function renderDagViz(forJob) {

//
resizeSvg(svg);
}

//
styleDagViz(forJob);
function postProcessDagVizForJob() {
}

function postProcessDagVizForStage() {
// Round corners on RDDs on the stage page
graphContainer()
.selectAll("svg.stage g.node rect")
.attr("rx", "5")
.attr("ry", "5");
}

/*
Expand Down Expand Up @@ -216,6 +221,7 @@ function renderDagVizForJob(svgContainer) {
var crossStageEdges = [];

metadataContainer().selectAll(".stage-metadata").each(function(d, i) {
// Set up container
var metadata = d3.select(this);
var dot = metadata.select(".dot-file").text();
var stageId = metadata.attr("stageId");
Expand All @@ -226,11 +232,14 @@ function renderDagVizForJob(svgContainer) {
var container = svgContainer
.append("a").attr("xlink:href", stageLink)
.append("g").attr("id", containerId);

// Now we need to shift the container for this stage so it doesn't overlap
// with existing ones. We do not need to do this for the first stage.
if (i > 0) {
// Take into account the position and width of the last stage's container
var existingStages = stageClusters();
var existingStages = graphContainer()
.selectAll("svg g.cluster")
.filter("[id*=\"" + VizConstants.stageClusterPrefix + "\"]");
if (!existingStages.empty()) {
var lastStage = d3.select(existingStages[0].pop());
var lastStageId = lastStage.attr("id");
Expand Down Expand Up @@ -262,25 +271,6 @@ function renderDagVizForJob(svgContainer) {
connectRDDs(fromRDDId, toRDDId, container);
}
}
}

/* Render the dot file as an SVG in the given container. */
function renderDot(dot, container) {
var escaped_dot = dot
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, "\"");
var g = graphlibDot.read(escaped_dot);
var renderer = new dagreD3.render();
renderer(container, g);
}

/* Style the visualization we just rendered. */
function styleDagViz(forJob) {
graphContainer()
.selectAll("svg path")
.style("stroke", VizConstants.edgeColor)
.style("stroke-width", VizConstants.edgeWidth);

// Put an arrow at the end of every edge
// We need to do this because we manually render some edges ourselves
Expand All @@ -289,77 +279,19 @@ function styleDagViz(forJob) {
graphContainer().select("svg")
.append(function() { return dagreD3Marker.cloneNode(true); })
.attr("id", "marker-arrow")
.select("path")
.attr("fill", VizConstants.edgeColor)
.attr("strokeWidth", "0px");
graphContainer().selectAll("svg g > path").attr("marker-end", "url(#marker-arrow)");
graphContainer().selectAll("svg g.edgePaths def").remove(); // We no longer need these

// Apply any job or stage specific styles
if (forJob) {
styleDagVizForJob();
} else {
styleDagVizForStage();
}
}

/* Apply job-page-specific style to the visualization. */
function styleDagVizForJob() {
graphContainer()
.selectAll("svg g.cluster rect")
.style("fill", "white")
.style("stroke", VizConstants.rddOperationColor)
.style("stroke-width", "4px")
.style("stroke-opacity", "0.5");
stageClusters()
.select("rect")
.style("stroke", VizConstants.stageColor)
.style("strokeWidth", "6px");
graphContainer()
.selectAll("svg g.node circle")
.style("fill", VizConstants.rddColor);
// TODO: add a legend to explain what a highlighted dot means
graphContainer()
.selectAll("svg g.cached circle")
.style("fill", VizConstants.rddCachedColor);
graphContainer()
.selectAll("svg g#cross-stage-edges path")
.style("fill", "none");
graphContainer()
.selectAll("svg g.cluster text")
.attr("fill", VizConstants.clusterLabelColor)
.attr("font-size", "11px");
}

/* Apply stage-page-specific style to the visualization. */
function styleDagVizForStage() {
graphContainer()
.selectAll("svg g.cluster rect")
.style("fill", "#F0F8FF")
.style("stroke", VizConstants.rddOperationColor)
.style("stroke-width", "4px")
.style("stroke-opacity", "0.5");
stageClusters()
.select("rect")
.style("fill", "white")
.style("stroke", VizConstants.stageColor)
.style("strokeWidth", "6px");
graphContainer()
.selectAll("svg g.node rect")
.style("fill", "#444444")
.attr("rx", "5") // round corners
.attr("ry", "5");
graphContainer()
.selectAll("svg g.cached rect")
.style("fill", VizConstants.rddCachedColor)
graphContainer()
.selectAll("svg g.node g.label text tspan")
.style("fill", "white");
graphContainer()
.selectAll("svg g.cluster text")
.attr("fill", "#444444")
.attr("font-size", "14px")
.attr("font-weight", "bold")
/* Render the dot file as an SVG in the given container. */
function renderDot(dot, container) {
var escaped_dot = dot
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, "\"");
var g = graphlibDot.read(escaped_dot);
var renderer = new dagreD3.render();
renderer(container, g);
}

/*
Expand All @@ -371,10 +303,8 @@ function getAbsolutePosition(d3selection) {
throw "Attempted to get absolute position of an empty selection.";
}
var obj = d3selection;
var _x = obj.attr("x") || 0,
_y = obj.attr("y") || 0;
_x = toFloat(_x);
_y = toFloat(_y);
var _x = toFloat(obj.attr("x")) || 0;
var _y = toFloat(obj.attr("y")) || 0;
while (!obj.empty()) {
var transformText = obj.attr("transform");
if (transformText) {
Expand All @@ -395,7 +325,7 @@ function getAbsolutePosition(d3selection) {
/* (Job page only) Connect two RDD nodes with a curved edge. */
function connectRDDs(fromRDDId, toRDDId, container) {
var fromNodeId = VizConstants.nodePrefix + fromRDDId;
var toNodeId = VizConstants.nodePrefix + toRDDId
var toNodeId = VizConstants.nodePrefix + toRDDId;
var fromPos = getAbsolutePosition(graphContainer().select("#" + fromNodeId));
var toPos = getAbsolutePosition(graphContainer().select("#" + toNodeId));

Expand Down Expand Up @@ -445,15 +375,12 @@ function connectRDDs(fromRDDId, toRDDId, container) {
container.append("path").datum(points).attr("d", line);
}

/* Helper d3 accessor to clusters that represent stages. */
function stageClusters() {
return graphContainer()
.selectAll("svg g.cluster")
.filter("[id*=\"" + VizConstants.stageClusterPrefix + "\"]");
}

/* Helper method to convert attributes to numeric values. */
function toFloat(f) {
return parseFloat(f.toString().replace(/px$/, ""));
if (f) {
return parseFloat(f.toString().replace(/px$/, ""));
} else {
return f;
}
}

12 changes: 5 additions & 7 deletions core/src/main/scala/org/apache/spark/ui/UIUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,10 @@ private[spark] object UIUtils extends Logging {

def commonHeaderNodes: Seq[Node] = {
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href={prependBaseUri("/static/bootstrap.min.css")}
type="text/css" />
<link rel="stylesheet" href={prependBaseUri("/static/webui.css")}
type="text/css" />
<link rel="stylesheet" href={prependBaseUri("/static/vis.min.css")}
typ="text/css" />
<link rel="stylesheet" href={prependBaseUri("/static/timeline-view.css")}></link>
<link rel="stylesheet" href={prependBaseUri("/static/bootstrap.min.css")} type="text/css" />
<link rel="stylesheet" href={prependBaseUri("/static/webui.css")} type="text/css" />
<link rel="stylesheet" href={prependBaseUri("/static/vis.min.css")} type="text/css" />
<link rel="stylesheet" href={prependBaseUri("/static/timeline-view.css")} type="text/css" />
<script src={prependBaseUri("/static/sorttable.js")} ></script>
<script src={prependBaseUri("/static/jquery-1.11.1.min.js")}></script>
<script src={prependBaseUri("/static/vis.min.js")}></script>
Expand All @@ -174,6 +171,7 @@ private[spark] object UIUtils extends Logging {
}

def vizHeaderNodes: Seq[Node] = {
<link rel="stylesheet" href={prependBaseUri("/static/spark-dag-viz.css")} type="text/css" />
<script src={prependBaseUri("/static/d3.min.js")}></script>
<script src={prependBaseUri("/static/dagre-d3.min.js")}></script>
<script src={prependBaseUri("/static/graphlib-dot.min.js")}></script>
Expand Down

0 comments on commit 9dab5f0

Please sign in to comment.