azkaban-aplcache
Changes
src/java/azkaban/flow/Edge.java 55(+54 -1)
src/java/azkaban/flow/Flow.java 6(+3 -3)
src/java/azkaban/flow/layout/DummyNode.java 17(+17 -0)
src/java/azkaban/flow/layout/LayeredFlowLayout.java 280(+182 -98)
src/web/css/azkaban.css 42(+41 -1)
src/web/js/azkaban.flow.view.js 219(+211 -8)
src/web/js/svgNavigate.js 15(+5 -10)
Details
src/java/azkaban/flow/Edge.java 55(+54 -1)
diff --git a/src/java/azkaban/flow/Edge.java b/src/java/azkaban/flow/Edge.java
index 3ac653b..06d8820 100644
--- a/src/java/azkaban/flow/Edge.java
+++ b/src/java/azkaban/flow/Edge.java
@@ -1,6 +1,10 @@
package azkaban.flow;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public class Edge {
private final String sourceId;
@@ -9,6 +13,9 @@ public class Edge {
private Node target;
private String error;
+ // Useful in rendering.
+ private String guideType;
+ private List<Point2D> guideValues;
public Edge(String fromId, String toId) {
this.sourceId = fromId;
@@ -60,6 +67,19 @@ public class Edge {
public void setTarget(Node target) {
this.target = target;
}
+
+ public String getGuideType() {
+ return guideType;
+ }
+
+ public List<Point2D> getGuideValues() {
+ return guideValues;
+ }
+
+ public void setGuides(String type, List<Point2D> values) {
+ this.guideType = type;
+ this.guideValues = values;
+ }
public Object toObject() {
HashMap<String, Object> obj = new HashMap<String, Object>();
@@ -68,12 +88,27 @@ public class Edge {
if (error != null) {
obj.put("error", error);
}
+ if (guideValues != null) {
+ HashMap<String, Object> lineGuidesObj = new HashMap<String, Object>();
+ lineGuidesObj.put("type", guideType);
+
+ ArrayList<Object> guides = new ArrayList<Object>();
+ for (Point2D point: this.guideValues) {
+ HashMap<String, Double> pointObj = new HashMap<String, Double>();
+ pointObj.put("x", point.getX());
+ pointObj.put("y", point.getY());
+ guides.add(pointObj);
+ }
+ lineGuidesObj.put("values", guides);
+
+ obj.put("guides",lineGuidesObj);
+ }
return obj;
}
+ @SuppressWarnings("unchecked")
public static Edge fromObject(Object obj) {
- @SuppressWarnings("unchecked")
HashMap<String, Object> edgeObj = (HashMap<String,Object>)obj;
String source = (String)edgeObj.get("source");
@@ -84,6 +119,24 @@ public class Edge {
Edge edge = new Edge(source, target);
edge.setError(error);
+ if (edgeObj.containsKey("guides")) {
+ Map<String, Object> guideMap = (Map<String,Object>)edgeObj.get("guides");
+ List<Object> values = (List<Object>)guideMap.get("values");
+ String type = (String)guideMap.get("type");
+
+ ArrayList<Point2D> valuePoints = new ArrayList<Point2D>();
+ for (Object pointObj: values) {
+ Map<String, Double> point = (Map<String,Double>)pointObj;
+
+ Double x = point.get("x");
+ Double y = point.get("y");
+
+ valuePoints.add(new Point2D.Double(x, y));
+ }
+
+ edge.setGuides(type, valuePoints);
+ }
+
return edge;
}
src/java/azkaban/flow/Flow.java 6(+3 -3)
diff --git a/src/java/azkaban/flow/Flow.java b/src/java/azkaban/flow/Flow.java
index 4b657d9..a4ce3ba 100644
--- a/src/java/azkaban/flow/Flow.java
+++ b/src/java/azkaban/flow/Flow.java
@@ -285,15 +285,15 @@ public class Flow {
this.isLayedOut = layedOut;
}
- /*package*/ Map<String, Node> getNodeMap() {
+ public Map<String, Node> getNodeMap() {
return nodes;
}
- /*package*/ Map<String, Set<Edge>> getOutEdgeMap() {
+ public Map<String, Set<Edge>> getOutEdgeMap() {
return outEdges;
}
- /*package*/ Map<String, Set<Edge>> getInEdgeMap() {
+ public Map<String, Set<Edge>> getInEdgeMap() {
return inEdges;
}
}
\ No newline at end of file
diff --git a/src/java/azkaban/flow/layout/BlockFlowLayout.java b/src/java/azkaban/flow/layout/BlockFlowLayout.java
new file mode 100644
index 0000000..3cd4cd4
--- /dev/null
+++ b/src/java/azkaban/flow/layout/BlockFlowLayout.java
@@ -0,0 +1,78 @@
+package azkaban.flow.layout;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import azkaban.flow.Edge;
+import azkaban.flow.Flow;
+import azkaban.flow.Node;
+
+public class BlockFlowLayout implements FlowLayout {
+
+ @Override
+ public void layoutFlow(Flow flow) {
+// createLayeredGraph(flow);
+ }
+
+// private void createLayeredGraph(Flow flow) {
+// HashMap<String, LayeredNode> layeredNode = new HashMap<String, LayeredNode>();
+//
+// for(Node node: flow.getStartNodes()) {
+// WrappedNode wnode = new WrappedNode(node);
+// layeredNode.put(wnode.getId(), wnode);
+// wnode.setLevel(0);
+// traverseNodes(layeredNode, flow, wnode);
+// }
+// }
+//
+// private void traverseNodes(HashMap<String, LayeredNode> layeredNode, Flow flow, LayeredNode wnode) {
+// Set<Edge> outEdges = flow.getOutEdges(wnode.getId());
+// Set<Edge> inEdges = flow.getInEdges(wnode.getId());
+// if (outEdges == null) {
+// return;
+// }
+//
+// if (outEdges.size() == 1 && inEdges != null && inEdges.size() == 1) {
+// System.out.println("Starting chain for " + wnode.getId());
+// ChainedNode cnode = new ChainedNode();
+// wnode.addOutNode(cnode);
+// cnode.addInNode(wnode);
+//
+// while(outEdges.size() == 1 && inEdges.size() == 1) {
+// Edge edge = outEdges.iterator().next();
+// if (layeredNode.containsKey(edge.getTargetId())) {
+// return;
+// }
+//
+// Node target = edge.getTarget();
+// cnode.addNode(new WrappedNode(target));
+// outEdges = flow.getOutEdges(target.getId());
+// layeredNode.put(target.getId(), cnode);
+//
+// System.out.println("Chain " + target.getId());
+//
+// if (outEdges == null) {
+// return;
+// }
+// inEdges = flow.getInEdges(target.getId());
+// }
+//
+// traverseNodes(layeredNode, flow, cnode);
+// }
+// else {
+// for (Edge edge: outEdges) {
+// if (layeredNode.containsKey(edge.getTargetId())) {
+// continue;
+// }
+//
+// WrappedNode child = new WrappedNode(edge.getTarget());
+// layeredNode.put(child.getId(), child);
+// wnode.addOutNode(child);
+// child.addInNode(wnode);
+//
+// traverseNodes(layeredNode, flow, child);
+// }
+// }
+//
+// }
+}
diff --git a/src/java/azkaban/flow/layout/ChainedEdge.java b/src/java/azkaban/flow/layout/ChainedEdge.java
new file mode 100644
index 0000000..5e41b5f
--- /dev/null
+++ b/src/java/azkaban/flow/layout/ChainedEdge.java
@@ -0,0 +1,5 @@
+package azkaban.flow.layout;
+
+public class ChainedEdge {
+
+}
src/java/azkaban/flow/layout/DummyNode.java 17(+17 -0)
diff --git a/src/java/azkaban/flow/layout/DummyNode.java b/src/java/azkaban/flow/layout/DummyNode.java
new file mode 100644
index 0000000..b97ea6f
--- /dev/null
+++ b/src/java/azkaban/flow/layout/DummyNode.java
@@ -0,0 +1,17 @@
+package azkaban.flow.layout;
+
+import azkaban.flow.Edge;
+
+public class DummyNode extends LayeredNode {
+ private Edge edge;
+ public DummyNode(Edge edge) {
+ super();
+ this.setEdge(edge);
+ }
+ public Edge getEdge() {
+ return edge;
+ }
+ public void setEdge(Edge edge) {
+ this.edge = edge;
+ }
+}
\ No newline at end of file
diff --git a/src/java/azkaban/flow/layout/LayeredNode.java b/src/java/azkaban/flow/layout/LayeredNode.java
new file mode 100644
index 0000000..e75aeb1
--- /dev/null
+++ b/src/java/azkaban/flow/layout/LayeredNode.java
@@ -0,0 +1,70 @@
+package azkaban.flow.layout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class LayeredNode {
+ private int level;
+ private ArrayList<LayeredNode> inNodes;
+ private ArrayList<LayeredNode> outNodes;
+ private double minX;
+ private double maxX;
+ private double x;
+ private double y;
+
+ public LayeredNode() {
+ inNodes = new ArrayList<LayeredNode>();
+ outNodes = new ArrayList<LayeredNode>();
+ }
+
+ public String getId() {
+ return "dummy";
+ }
+
+ public int getLevel() {
+ return level;
+ }
+ public void setLevel(int level) {
+ this.level = level;
+ }
+ public double getX() {
+ return x;
+ }
+ public void setX(double x) {
+ this.x = x;
+ }
+ public double getMinX() {
+ return minX;
+ }
+ public void setMinX(double min) {
+ minX = min;
+ }
+ public double getMaxX() {
+ return maxX;
+ }
+ public void setMaxX(double max) {
+ this.maxX = max;
+ }
+ public void setY(double y) {
+ this.y = y;
+ }
+ public double getY() {
+ return y;
+ }
+
+ public void addInNode(LayeredNode node) {
+ inNodes.add(node);
+ }
+
+ public void addOutNode(LayeredNode node) {
+ outNodes.add(node);
+ }
+
+ public List<LayeredNode> getInNode() {
+ return inNodes;
+ }
+
+ public List<LayeredNode> getOutNode() {
+ return outNodes;
+ }
+}
\ No newline at end of file
diff --git a/src/java/azkaban/flow/layout/WrappedNode.java b/src/java/azkaban/flow/layout/WrappedNode.java
new file mode 100644
index 0000000..1dd6d67
--- /dev/null
+++ b/src/java/azkaban/flow/layout/WrappedNode.java
@@ -0,0 +1,18 @@
+package azkaban.flow.layout;
+
+import azkaban.flow.Node;
+
+public class WrappedNode extends LayeredNode {
+ private Node node;
+ public WrappedNode(Node node) {
+ this.node = node;
+ setLevel(node.getLevel());
+ }
+ public Node getNode() {
+ return node;
+ }
+ @Override
+ public String getId() {
+ return node.getId();
+ }
+}
diff --git a/src/java/azkaban/flow/layout2/BlockFlowLayout.java b/src/java/azkaban/flow/layout2/BlockFlowLayout.java
new file mode 100644
index 0000000..8447d66
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/BlockFlowLayout.java
@@ -0,0 +1,54 @@
+package azkaban.flow.layout2;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
+import azkaban.flow.Edge;
+import azkaban.flow.Flow;
+import azkaban.flow.Node;
+import azkaban.flow.layout.FlowLayout;
+
+public class BlockFlowLayout implements FlowLayout {
+
+ @Override
+ public void layoutFlow(Flow flow) {
+ HashMap<String, LayeredNode> layoutNodes = new HashMap<String, LayeredNode>();
+
+ for (Node node: flow.getNodes()) {
+ layoutNodes.put(node.getId(), new WrappedNode(node));
+ }
+
+ for(Node node: flow.getStartNodes()) {
+ WrappedNode wnode = new WrappedNode(node);
+ traverseAndChain(layoutNodes, flow, wnode);
+ }
+ }
+
+ public void traverseAndChain(HashMap<String, LayeredNode> layoutNodes, Flow flow, LayeredNode node) {
+ if (layoutNodes.containsKey(node.getId())) {
+ return;
+ }
+
+ layoutNodes.put(node.getId(), node);
+
+ HashMap<String, GroupedEdge> groupEdge = new HashMap<String, GroupedEdge>();
+ for(Edge edge: flow.getOutEdges(node.getId())) {
+ Node dest = edge.getTarget();
+ Set<Edge> destOutEdge = flow.getOutEdges(dest.getId());
+ Set<Edge> destInEdge = flow.getInEdges(dest.getId());
+
+ ArrayList<Node> chainDest = new ArrayList<Node>();
+ while(destOutEdge != null && destOutEdge.size()==1 && destInEdge.size()==1) {
+ chainDest.add(dest);
+ dest = edge.getTarget();
+ destOutEdge = flow.getOutEdges(dest.getId());
+ destInEdge = flow.getInEdges(dest.getId());
+ }
+
+
+ }
+
+ }
+
+}
diff --git a/src/java/azkaban/flow/layout2/ChainedEdge.java b/src/java/azkaban/flow/layout2/ChainedEdge.java
new file mode 100644
index 0000000..bca120f
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/ChainedEdge.java
@@ -0,0 +1,37 @@
+package azkaban.flow.layout2;
+
+import java.util.ArrayList;
+
+public class ChainedEdge {
+ private LayeredNode start;
+ private LayeredNode end;
+ private ArrayList<LayeredNode> chain;
+ private String chainString = null;
+
+ public ChainedEdge(LayeredNode start, LayeredNode end) {
+ this.start = start;
+ this.end = end;
+ chain = new ArrayList<LayeredNode>();
+ }
+
+ public LayeredNode getStart() {
+ return start;
+ }
+
+ public LayeredNode getEnd() {
+ return end;
+ }
+
+ public void addChain(ArrayList<LayeredNode> chain) {
+ this.chain = chain;
+ StringBuffer chainString = new StringBuffer();
+ chainString.append(start.getId());
+
+
+ chainString.append(end.getId());
+ }
+
+ public String chainString() {
+ return chainString;
+ }
+}
diff --git a/src/java/azkaban/flow/layout2/GroupedEdge.java b/src/java/azkaban/flow/layout2/GroupedEdge.java
new file mode 100644
index 0000000..20d8222
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/GroupedEdge.java
@@ -0,0 +1,35 @@
+package azkaban.flow.layout2;
+
+import java.util.ArrayList;
+
+public class GroupedEdge {
+ private WrappedNode source;
+ private WrappedNode dest;
+ private ArrayList<ChainedEdge> edges;
+
+ public GroupedEdge(WrappedNode source, WrappedNode dest) {
+ this.setSource(source);
+ this.setDest(dest);
+ this.edges = new ArrayList<ChainedEdge>();
+ }
+
+ public WrappedNode getSource() {
+ return source;
+ }
+
+ public void setSource(WrappedNode source) {
+ this.source = source;
+ }
+
+ public WrappedNode getDest() {
+ return dest;
+ }
+
+ public void setDest(WrappedNode dest) {
+ this.dest = dest;
+ }
+
+ public void addNestedEdge(ChainedEdge edge) {
+ edges.add(edge);
+ }
+}
diff --git a/src/java/azkaban/flow/layout2/LayeredEdge.java b/src/java/azkaban/flow/layout2/LayeredEdge.java
new file mode 100644
index 0000000..c1ee127
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/LayeredEdge.java
@@ -0,0 +1,11 @@
+package azkaban.flow.layout2;
+
+public class LayeredEdge {
+ private LayeredNode source;
+ private LayeredNode dest;
+
+ public LayeredEdge(LayeredNode source, LayeredNode dest) {
+ this.source = source;
+ this.dest = dest;
+ }
+}
diff --git a/src/java/azkaban/flow/layout2/LayeredNode.java b/src/java/azkaban/flow/layout2/LayeredNode.java
new file mode 100644
index 0000000..732cf77
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/LayeredNode.java
@@ -0,0 +1,72 @@
+package azkaban.flow.layout2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import azkaban.flow.Edge;
+
+public abstract class LayeredNode {
+ private int level = -1;
+ private ArrayList<ChainedEdge> inEdges;
+ private ArrayList<ChainedEdge> outEdges;
+ private double minX;
+ private double maxX;
+ private double x;
+ private double y;
+
+ public LayeredNode() {
+ inEdges = new ArrayList<ChainedEdge>();
+ outEdges = new ArrayList<ChainedEdge>();
+ }
+
+ public String getId() {
+ return "dummy";
+ }
+
+ public int getLevel() {
+ return level;
+ }
+ public void setLevel(int level) {
+ this.level = level;
+ }
+ public double getX() {
+ return x;
+ }
+ public void setX(double x) {
+ this.x = x;
+ }
+ public double getMinX() {
+ return minX;
+ }
+ public void setMinX(double min) {
+ minX = min;
+ }
+ public double getMaxX() {
+ return maxX;
+ }
+ public void setMaxX(double max) {
+ this.maxX = max;
+ }
+ public void setY(double y) {
+ this.y = y;
+ }
+ public double getY() {
+ return y;
+ }
+
+ public void addInEdge(ChainedEdge edge) {
+ inEdges.add(edge);
+ }
+
+ public void addOutNode(ChainedEdge edge) {
+ outEdges.add(edge);
+ }
+
+ public List<ChainedEdge> getInEdges() {
+ return inEdges;
+ }
+
+ public List<ChainedEdge> getOutNode() {
+ return outEdges;
+ }
+}
\ No newline at end of file
diff --git a/src/java/azkaban/flow/layout2/NestedEdge.java b/src/java/azkaban/flow/layout2/NestedEdge.java
new file mode 100644
index 0000000..7a96b32
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/NestedEdge.java
@@ -0,0 +1,5 @@
+package azkaban.flow.layout2;
+
+public class NestedEdge {
+
+}
diff --git a/src/java/azkaban/flow/layout2/NestedLayoutGraph.java b/src/java/azkaban/flow/layout2/NestedLayoutGraph.java
new file mode 100644
index 0000000..628915b
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/NestedLayoutGraph.java
@@ -0,0 +1,34 @@
+package azkaban.flow.layout2;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import azkaban.flow.Edge;
+import azkaban.flow.Flow;
+import azkaban.flow.Node;
+
+public class NestedLayoutGraph {
+ private Flow flow;
+ private HashMap<String, LayeredNode> nodes = new HashMap<String, LayeredNode>();
+ private HashMap<String, LayeredEdge> edges = new HashMap<String, LayeredEdge>();
+ private HashMap<String, ArrayList<LayeredEdge>> inEdges;
+ private HashMap<String, ArrayList<LayeredEdge>> outEdges;
+
+ public NestedLayoutGraph(Flow flow) {
+ this.flow = flow;
+ }
+
+ private void setupGraph() {
+ for (Node node: flow.getNodes()) {
+ nodes.put(node.getId(), new WrappedNode(node));
+ }
+
+ for (Edge edge: flow.getEdges()) {
+ LayeredEdge ledge = new LayeredEdge(nodes.get(edge.getSourceId()), nodes.get(edge.getTargetId()));
+ edges.put(edge.getId(), ledge);
+
+ //inEdges.put(edge.getSourceId(), ledge);
+ //outEdges.put(edge.getTargetId(), ledge);
+ }
+ }
+}
diff --git a/src/java/azkaban/flow/layout2/NestedNode.java b/src/java/azkaban/flow/layout2/NestedNode.java
new file mode 100644
index 0000000..25c9600
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/NestedNode.java
@@ -0,0 +1,5 @@
+package azkaban.flow.layout2;
+
+public class NestedNode extends LayeredNode {
+
+}
diff --git a/src/java/azkaban/flow/layout2/WrappedNode.java b/src/java/azkaban/flow/layout2/WrappedNode.java
new file mode 100644
index 0000000..afc3cc9
--- /dev/null
+++ b/src/java/azkaban/flow/layout2/WrappedNode.java
@@ -0,0 +1,25 @@
+package azkaban.flow.layout2;
+
+import azkaban.flow.Node;
+
+public class WrappedNode extends LayeredNode {
+ private Node node;
+ private boolean visited=false;
+ public WrappedNode(Node node) {
+ this.node = node;
+ }
+ public Node getNode() {
+ return node;
+ }
+ @Override
+ public String getId() {
+ return node.getId();
+ }
+ public boolean isVisited() {
+ return visited;
+ }
+ public void setVisited(boolean visited) {
+ this.visited = visited;
+ }
+
+}
diff --git a/src/java/azkaban/project/FileProjectManager.java b/src/java/azkaban/project/FileProjectManager.java
index 22b7efc..bbbbc38 100644
--- a/src/java/azkaban/project/FileProjectManager.java
+++ b/src/java/azkaban/project/FileProjectManager.java
@@ -16,7 +16,8 @@ import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import azkaban.flow.Flow;
-import azkaban.flow.LayeredFlowLayout;
+import azkaban.flow.layout.BlockFlowLayout;
+import azkaban.flow.layout.LayeredFlowLayout;
import azkaban.user.Permission;
import azkaban.user.Permission.Type;
import azkaban.user.User;
@@ -129,6 +130,9 @@ public class FileProjectManager implements ProjectManager {
LayeredFlowLayout layout = new LayeredFlowLayout();
layout.layoutFlow(flow);
+ BlockFlowLayout bfl = new BlockFlowLayout();
+ bfl.layoutFlow(flow);
+
try {
writeFlowFile(flowFile.getParentFile(), flow);
} catch (IOException e) {
diff --git a/src/java/azkaban/webapp/servlet/IndexServlet.java b/src/java/azkaban/webapp/servlet/IndexServlet.java
index 0e3a676..0af2f2f 100644
--- a/src/java/azkaban/webapp/servlet/IndexServlet.java
+++ b/src/java/azkaban/webapp/servlet/IndexServlet.java
@@ -56,7 +56,7 @@ public class IndexServlet extends LoginAbstractAzkabanServlet {
if(hasParam(req, "action")) {
String action = getParam(req, "action");
if (action.equals("create")) {
-
+
}
}
else {
diff --git a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index 986a8e7..b389b08 100644
--- a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -1,5 +1,6 @@
package azkaban.webapp.servlet;
+import java.awt.geom.Point2D;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -157,6 +158,20 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
if (edge.hasError()) {
edgeObj.put("error", edge.getError());
}
+ if (edge.getGuideValues() != null) {
+ List<Point2D> guides = edge.getGuideValues();
+ ArrayList<Object> guideOutput = new ArrayList<Object>();
+ for (Point2D guide: guides) {
+ double x = guide.getX();
+ double y = guide.getY();
+ HashMap<String, Double> point = new HashMap<String, Double>();
+ point.put("x", x);
+ point.put("y", y);
+ guideOutput.add(point);
+ }
+
+ edgeObj.put("guides", guideOutput);
+ }
edgeList.add(edgeObj);
}
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index 6d3f7bd..853549f 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -53,10 +53,14 @@
<div id="graphView">
<div class="relative">
<div id="jobList">
- Loading Flow ${flowid}
+ <div id="filterList">
+ <input id="filter" placeholder=" Job Filter" />
+ </div>
+ <div id="list">
+ </div>
</div>
<div id="svgDiv" >
- <svg id="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100% 100%" >
+ <svg id="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
</svg>
</div>
</div>
src/web/css/azkaban.css 42(+41 -1)
diff --git a/src/web/css/azkaban.css b/src/web/css/azkaban.css
index 0b640e8..2959b80 100644
--- a/src/web/css/azkaban.css
+++ b/src/web/css/azkaban.css
@@ -989,7 +989,47 @@ tr:hover td {
top: 0px;
left: 0px;
height: 100%;
- width: 260px;
+ width: 250px;
+}
+
+#list {
+ position: absolute;
+ background-color: #fff;
+ margin-right: 10px;
+ border: solid;
+ border-color: #CCC;
+ border-width: 1px;
+ overflow: auto;
+ top: 26px;
+ width: 100%;
+ bottom: 120px;
+}
+
+#filter {
+ width: 100%;
+}
+
+#list ul {
+ white-space: nowrap
+}
+
+#list ul li {
+ margin: 4px 5px;
+ border-bottom: 1px solid #EEE;
+}
+
+#list ul li a {
+ font-size: 10pt;
+ margin-left: 5px;
+ cursor: pointer;
+}
+
+#list ul li a span {
+ background-color: #FF0;
+}
+
+#list ul li a:hover {
+ color: #009FC9;
}
/* old styles */
src/web/js/azkaban.flow.view.js 219(+211 -8)
diff --git a/src/web/js/azkaban.flow.view.js b/src/web/js/azkaban.flow.view.js
index 5129e40..c2e4d2f 100644
--- a/src/web/js/azkaban.flow.view.js
+++ b/src/web/js/azkaban.flow.view.js
@@ -35,6 +35,104 @@ azkaban.FlowTabView= Backbone.View.extend({
}
});
+var jobListView;
+azkaban.JobListView = Backbone.View.extend({
+ events: {
+ "keyup input": "filterJobs"
+ },
+ initialize: function(settings) {
+ this.model.bind('change:selected', this.changeSelected, this);
+ this.model.bind('change:graph', this.render, this);
+ },
+ filterJobs: function(self) {
+ var filter = $("#filter").val();
+
+ if (filter && filter.trim() != "") {
+ filter = filter.trim();
+
+ if (filter == "") {
+ if (this.filter) {
+ $("#jobs").children().each(
+ function(){
+ var a = $(this).find("a");
+ $(a).html(this.jobid);
+ }
+ );
+ }
+
+ this.filter = null;
+ return;
+ }
+ }
+ else {
+ if (this.filter) {
+ $("#jobs").children().each(
+ function(){
+ var a = $(this).find("a");
+ $(a).html(this.jobid);
+ }
+ );
+ }
+
+ this.filter = null;
+ return;
+ }
+
+ $("#jobs").children().each(
+ function(){
+ var jobid = this.jobid;
+ var index = jobid.indexOf(filter);
+ if (index != -1) {
+ var a = $(this).find("a");
+
+ var endIndex = index + filter.length;
+ var newHTML = jobid.substring(0, index) + "<span>" + jobid.substring(index, endIndex) + "</span>" + jobid.substring(endIndex, jobid.length);
+
+ $(a).html(newHTML);
+ $(this).show();
+ }
+ else {
+ $(this).hide();
+ }
+ });
+
+ this.filter = filter;
+ },
+ render: function(self) {
+ var data = this.model.get("data");
+ var nodes = data.nodes;
+ var edges = data.edges;
+ if (nodes.length == 0) {
+ console.log("No results");
+ return;
+ };
+
+ var nodeArray = nodes.slice(0);
+ nodeArray.sort(function(a,b){
+ var diff = a.y - b.y;
+ if (diff == 0) {
+ return a.x - b.x;
+ }
+ else {
+ return diff;
+ }
+ });
+
+ var ul = document.createElement("ul");
+ $(ul).attr("id", "jobs");
+ for (var i = 0; i < nodeArray.length; ++i) {
+ var li = document.createElement("li");
+ var a = document.createElement("a");
+ $(a).text(nodeArray[i].id);
+ li.appendChild(a);
+ ul.appendChild(li);
+ li.jobid=nodeArray[i].id;
+ }
+
+ $("#list").append(ul);
+ }
+});
+
var svgGraphView;
azkaban.SvgGraphView = Backbone.View.extend({
events: {
@@ -57,38 +155,142 @@ azkaban.SvgGraphView = Backbone.View.extend({
$(svg).svgNavigate();
},
+ initializeDefs: function(self) {
+ var def = document.createElementNS(svgns, 'defs');
+ def.setAttributeNS(null, "id", "buttonDefs");
+
+ // ArrowHead
+ var arrowHeadMarker = document.createElementNS(svgns, 'marker');
+ arrowHeadMarker.setAttribute("id", "triangle");
+ arrowHeadMarker.setAttribute("viewBox", "0 0 10 10");
+ arrowHeadMarker.setAttribute("refX", "5");
+ arrowHeadMarker.setAttribute("refY", "5");
+ arrowHeadMarker.setAttribute("markerUnits", "strokeWidth");
+ arrowHeadMarker.setAttribute("markerWidth", "4");
+ arrowHeadMarker.setAttribute("markerHeight", "3");
+ arrowHeadMarker.setAttribute("orient", "auto");
+ var path = document.createElementNS(svgns, 'polyline');
+ arrowHeadMarker.appendChild(path);
+ path.setAttribute("points", "0,0 10,5 0,10 1,5");
+
+ def.appendChild(arrowHeadMarker);
+
+ this.svgGraph.appendChild(def);
+ },
render: function(self) {
console.log("graph render");
var data = this.model.get("data");
var nodes = data.nodes;
+ var edges = data.edges;
+ if (nodes.length == 0) {
+ console.log("No results");
+ return;
+ };
+
+ var bounds = {};
+ this.nodes = {};
for (var i = 0; i < nodes.length; ++i) {
- this.drawNode(this, nodes[i]);
+ this.drawNode(this, nodes[i], bounds);
}
+
+ for (var i = 0; i < edges.length; ++i) {
+ this.drawEdge(this, edges[i]);
+ }
+
+ this.graphBounds = bounds;
+
+ $("#svgGraph").svgNavigate("transformToBox", {x: bounds.minX, y: bounds.minY, width: (bounds.maxX - bounds.minX), height: (bounds.maxY - bounds.minY) });
},
changeSelected: function(self) {
console.log("change selected");
},
- drawNode: function(self, node) {
+ drawEdge: function(self, edge) {
+ var svg = self.svgGraph;
+ var svgns = self.svgns;
+
+ var startNode = this.nodes[edge.from];
+ var endNode = this.nodes[edge.target];
+
+ if (edge.guides) {
+ var pointString = "" + startNode.sx + "," + startNode.sy + " ";
+
+ for (var i = 0; i < edge.guides.length; ++i ) {
+ edgeGuidePoint = edge.guides[i];
+ this.calcScalePoint(edgeGuidePoint);
+ pointString += edgeGuidePoint.sx + "," + edgeGuidePoint.sy + " ";
+ }
+
+ pointString += endNode.sx + "," + endNode.sy;
+ var polyLine = document.createElementNS(svgns, "polyline");
+ polyLine.setAttributeNS(null, "points", pointString);
+ polyLine.setAttributeNS(null, "style", "fill:none;stroke:black;stroke-width:3");
+ self.mainG.appendChild(polyLine);
+ }
+ else {
+ var line = document.createElementNS(svgns, 'line');
+ line.setAttributeNS(null, "x1", startNode.sx);
+ line.setAttributeNS(null, "y1", startNode.sy);
+
+ line.setAttributeNS(null, "x2", endNode.sx);
+ line.setAttributeNS(null, "y2", endNode.sy);
+ line.setAttributeNS(null, "style", "stroke:rgb(255,0,0);stroke-width:2");
+
+ self.mainG.appendChild(line);
+ }
+ },
+ drawNode: function(self, node, bounds) {
var svg = self.svgGraph;
var svgns = self.svgns;
+ this.calcScalePoint(node);
+ var xOffset = 10;
+ var yOffset = 10;
+
var nodeG = document.createElementNS(svgns, "g");
nodeG.setAttributeNS(null, "id", node.id);
nodeG.setAttributeNS(null, "font-family", "helvetica");
- nodeG.setAttributeNS(null, "transform", "translate(" + (node.x * 100) + "," + (node.y*100)+ ")");
+ nodeG.setAttributeNS(null, "transform", "translate(" + node.sx + "," + node.sy + ")");
+
+ var innerG = document.createElementNS(svgns, "g");
+ innerG.setAttributeNS(null, "transform", "translate(-10,-10)");
var rect1 = document.createElementNS(svgns, 'rect');
- rect1.setAttributeNS(null, "y", 2);
- rect1.setAttributeNS(null, "x", 2);
+ rect1.setAttributeNS(null, "y", 0);
+ rect1.setAttributeNS(null, "x", 0);
rect1.setAttributeNS(null, "ry", 12);
rect1.setAttributeNS(null, "width", 20);
- rect1.setAttributeNS(null, "height", 30);
- rect1.setAttributeNS(null, "style", "width:inherit;fill-opacity:1.0;stroke-opacity:1");
+ rect1.setAttributeNS(null, "height", 20);
+ rect1.setAttributeNS(null, "style", "width:inherit;stroke-opacity:1");
+
- nodeG.appendChild(rect1);
+ var text = document.createElementNS(svgns, 'text');
+ var textLabel = document.createTextNode(node.id);
+ text.appendChild(textLabel);
+ text.setAttributeNS(null, "x", 4);
+ text.setAttributeNS(null, "y", 0);
+ text.setAttributeNS(null, "height", 10);
+
+ this.addBounds(bounds, {minX:node.sx - xOffset, minY: node.sy - yOffset, maxX: node.sx + xOffset, maxY: node.sy + yOffset});
+
+ innerG.appendChild(rect1);
+ innerG.appendChild(text);
+ nodeG.appendChild(innerG);
self.mainG.appendChild(nodeG);
+
+ this.nodes[node.id] = node;
+ },
+ addBounds: function(toBounds, addBounds) {
+ toBounds.minX = toBounds.minX ? Math.min(toBounds.minX, addBounds.minX) : addBounds.minX;
+ toBounds.minY = toBounds.minY ? Math.min(toBounds.minY, addBounds.minY) : addBounds.minY;
+ toBounds.maxX = toBounds.maxX ? Math.max(toBounds.maxX, addBounds.maxX) : addBounds.maxX;
+ toBounds.maxY = toBounds.maxY ? Math.max(toBounds.maxY, addBounds.maxY) : addBounds.maxY;
+ },
+ calcScalePoint : function(pointObj) {
+ pointObj.sx = 50*pointObj.x;
+ pointObj.sy = 50*pointObj.y;
}
+
});
var graphModel;
@@ -113,6 +315,7 @@ $(function() {
graphModel = new azkaban.GraphModel();
svgGraphView = new azkaban.SvgGraphView({el:$('#svgDiv'), model: graphModel});
+ jobsListView = new azkaban.JobListView({el:$('#jobList'), model: graphModel});
var requestURL = contextURL + "/manager";
src/web/js/svgNavigate.js 15(+5 -10)
diff --git a/src/web/js/svgNavigate.js b/src/web/js/svgNavigate.js
index 71f1915..35426ea 100644
--- a/src/web/js/svgNavigate.js
+++ b/src/web/js/svgNavigate.js
@@ -48,6 +48,8 @@
evt = window.event;
}
var target = evt.target;
+
+
var leftOffset = 0;
var topOffset = 0;
if (!target.marker) {
@@ -58,13 +60,6 @@
target = target.farthestViewportElement;
}
- if (target.parentNode.offsetLeft) {
- leftOffset = target.parentNode.offsetLeft;
- }
- if (target.parentNode.offsetTop) {
- topOffset = target.parentNode.offsetTop;
- }
-
// Trackball/trackpad vs wheel. Need to accommodate
var delta = 0;
if (evt.wheelDelta) {
@@ -78,9 +73,9 @@
target.zoomIndex = zoomLevel;
var scale = target.zoomLevels[zoomLevel];
- var x = evt.clientX - leftOffset;
- var y = evt.clientY - topOffset;
-
+ var y = evt.layerY;
+ var x = evt.layerX;
+
scaleGraph(target, scale, x, y);
}