azkaban-developers

Details

diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index 2949769..3ae46d6 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -23,6 +23,7 @@
 
 		<script type="text/javascript" src="${context}/js/moment.min.js"></script>
 		<script type="text/javascript" src="${context}/js/bootstrap-datetimepicker.min.js"></script>
+		<script type="text/javascript" src="${context}/js/d3.v3.min.js"></script>
 		
     <script type="text/javascript" src="${context}/js/dust-full-2.2.3.min.js"></script>
 		<script type="text/javascript" src="${context}/js/flowsummary.js"></script>
@@ -33,6 +34,7 @@
 		<script type="text/javascript" src="${context}/js/azkaban/util/ajax.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/util/common.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/util/layout.js"></script>
+		<script type="text/javascript" src="${context}/js/azkaban/view/time-graph.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/flow.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/flow-stats.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/flow-job.js"></script>
@@ -50,6 +52,24 @@
 			var flowId = "${flowid}";
 			var execId = null;
 		</script>
+		<style>
+			.axis path,
+			.axis line {
+			  fill: none;
+			  stroke: #000;
+			  shape-rendering: crispEdges;
+			}
+			
+			.x.axis path {
+			  display: none;
+			}
+			
+			.line {
+			  fill: none;
+			  stroke: steelblue;
+			  stroke-width: 1.5px;
+			}
+		</style>
 		<link rel="stylesheet" type="text/css" href="${context}/css/azkaban-svg.css" />
 		<link rel="stylesheet" type="text/css" href="${context}/css/bootstrap-datetimepicker.css" />
 	</head>
@@ -109,6 +129,10 @@
     <div class="container-full" id="executionsView">
 			<div class="row">
 				<div class="col-xs-12">
+					<div class="well well-clear well-sm">
+            <div id="timeGraph"></div>
+          </div>
+
 					<table class="table table-striped table-bordered table-condensed table-hover" id="execTable">
 						<thead>
 							<tr>
diff --git a/src/java/azkaban/webapp/servlet/velocity/jobhistorypage.vm b/src/java/azkaban/webapp/servlet/velocity/jobhistorypage.vm
index 0623dea..48b7394 100644
--- a/src/java/azkaban/webapp/servlet/velocity/jobhistorypage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/jobhistorypage.vm
@@ -23,6 +23,7 @@
 
 		<script type="text/javascript" src="${context}/js/d3.v3.min.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/util/date.js"></script>
+		<script type="text/javascript" src="${context}/js/azkaban/view/time-graph.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/job-history.js"></script>
 		<script type="text/javascript">
 			var contextURL = "${context}";
diff --git a/src/web/js/azkaban/view/flow.js b/src/web/js/azkaban/view/flow.js
index 75303a9..ead141c 100644
--- a/src/web/js/azkaban/view/flow.js
+++ b/src/web/js/azkaban/view/flow.js
@@ -431,6 +431,8 @@ azkaban.SummaryModel = Backbone.Model.extend({});
 var flowStatsView;
 var flowStatsModel;
 
+var executionsTimeGraphView;
+
 var mainSvgGraphView;
 
 $(function() {
@@ -476,6 +478,12 @@ $(function() {
 		contextMenuCallback: exJobClickCallback
 	});
 	
+  executionsTimeGraphView = new azkaban.TimeGraphView({
+		el: $('#timeGraph'), 
+		model: executionModel,
+    modelField: 'executions'
+	});
+	
 	var requestURL = contextURL + "/manager";
 
 	// Set up the Flow options view. Create a new one every time :p
diff --git a/src/web/js/azkaban/view/job-history.js b/src/web/js/azkaban/view/job-history.js
index 9607258..a205d22 100644
--- a/src/web/js/azkaban/view/job-history.js
+++ b/src/web/js/azkaban/view/job-history.js
@@ -17,107 +17,6 @@
 $.namespace('azkaban');
 
 var jobHistoryView;
-azkaban.JobHistoryView = Backbone.View.extend({
-	events: {
-	},
-	
-	initialize: function(settings) {
-		this.render();
-	},
-	
-	render: function(self) {
-		var data = this.model.get("data");
-	
-		var margin = {
-			top: 20, 
-			right: 20, 
-			bottom: 30, 
-			left: 70
-		};
-	  var width = $(this.el).width() - margin.left - margin.right;
-	  var height = 300 - margin.top - margin.bottom;
-	    
-		var x = d3.time.scale()
-		    .range([0, width]);
-		
-		var y = d3.scale.linear()
-		    .range([height, 0]);
-	    
-		var xAxis = d3.svg.axis()
-				.scale(x)
-				.orient("bottom");
-
-		var yAxis = d3.svg.axis()
-		    .scale(y)
-		    .orient("left");
-		yAxis.tickFormat(
-			function(d) {
-				return formatDuration(d, 1);
-			}
-		);
-		
-		var line = d3.svg.line()
-		    .x(function(d) { return x(d.startTime); })
-		    .y(function(d) { return y(d.endTime - d.startTime); });
-		 
-		var svg = d3.select("#timeGraph").append("svg")
-		    .attr("width", width + margin.left + margin.right)
-		    .attr("height", height + margin.top + margin.bottom)
-		    .append("g")
-		    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
-		  
-		var xextent = d3.extent(data, function(d) {
-			return d.startTime;
-		});
-		var diff = (xextent[1] - xextent[0])*0.05;
-		
-		xextent[0] -= diff;
-		xextent[1] += diff;
-		x.domain(xextent);
-		
-		var yextent = d3.extent(data, function(d) {
-			return d.endTime - d.startTime;
-		});
-		var upperYbound = yextent[1]*1.25;
-		y.domain([0, upperYbound]);
-	
-		svg.append("g")
-				.attr("class", "x axis")
-				.attr("transform", "translate(0," + height + ")")
-				.call(xAxis);
-	
-		svg.append("g")
-				.attr("class", "y axis")
-				.call(yAxis)
-				.append("text")
-				.attr("transform", "rotate(-90)")
-				.attr("y", 6)
-				.attr("dy", ".71em")
-				.style("text-anchor", "end")
-				.text("Duration");
-	
-		svg.append("path")
-				.datum(data)
-				.attr("class", "line")
-				.attr("d", line);
-				
-		var node = svg.selectAll("g.node")
-				.data(data)
-				.attr("class", "node")
-				.enter().append("g")
-				.attr("transform",  function(d) {
-			return "translate(" + x(d.startTime) + "," + y(d.endTime-d.startTime) + ")";
-		});
-		
-		node.append("circle")
-				.attr("r", 5)
-				.attr("class", function(d) {return d.status;})
-				.append("svg:title")
-				.text(function(d) {
-			return d.execId + ":" + d.flowId + " ran in " + getDuration(d.startTime, d.endTime);
-		});
-	}
-});
 
 var dataModel;
 azkaban.DataModel = Backbone.Model.extend({});
@@ -129,8 +28,11 @@ $(function() {
 	dataModel.set({
 		"data": series
 	});
-	jobDurationView = new azkaban.JobHistoryView({
+  dataModel.trigger('render');
+
+	jobHistoryView = new azkaban.TimeGraphView({
 		el: $('#timeGraph'), 
-		model: dataModel
+		model: dataModel,
+    modelField: "data"
 	});
 });
diff --git a/src/web/js/azkaban/view/time-graph.js b/src/web/js/azkaban/view/time-graph.js
new file mode 100644
index 0000000..122cdad
--- /dev/null
+++ b/src/web/js/azkaban/view/time-graph.js
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012 LinkedIn Corp.
+ * 
+ * Licensed 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.
+ */
+
+$.namespace('azkaban');
+
+azkaban.TimeGraphView = Backbone.View.extend({
+	events: {
+	},
+	
+	initialize: function(settings) {
+		this.model.bind('render', this.render, this);
+		this.model.bind('change:page', this.render, this);
+    this.modelField = settings.modelField;
+    this.render();
+	},
+	
+	render: function(self) {
+		var data = this.model.get(this.modelField);
+    if (data == null) {
+      return;
+    }
+	
+		var margin = {
+			top: 20, 
+			right: 20, 
+			bottom: 30, 
+			left: 70
+		};
+	  var width = $(this.el).width() - margin.left - margin.right;
+	  var height = 300 - margin.top - margin.bottom;
+	    
+		var x = d3.time.scale()
+		    .range([0, width]);
+		
+		var y = d3.scale.linear()
+		    .range([height, 0]);
+	    
+		var xAxis = d3.svg.axis()
+				.scale(x)
+				.orient("bottom");
+
+		var yAxis = d3.svg.axis()
+		    .scale(y)
+		    .orient("left");
+		yAxis.tickFormat(
+			function(d) {
+				return formatDuration(d, 1);
+			}
+		);
+		
+		var line = d3.svg.line()
+		    .x(function(d) { return x(d.startTime); })
+		    .y(function(d) { return y(d.endTime - d.startTime); });
+		 
+		var svg = d3.select("#timeGraph").append("svg")
+		    .attr("width", width + margin.left + margin.right)
+		    .attr("height", height + margin.top + margin.bottom)
+		    .append("g")
+		    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+		  
+		var xextent = d3.extent(data, function(d) {
+			return d.startTime;
+		});
+		var diff = (xextent[1] - xextent[0])*0.05;
+		
+		xextent[0] -= diff;
+		xextent[1] += diff;
+		x.domain(xextent);
+		
+		var yextent = d3.extent(data, function(d) {
+			return d.endTime - d.startTime;
+		});
+		var upperYbound = yextent[1]*1.25;
+		y.domain([0, upperYbound]);
+	
+		svg.append("g")
+				.attr("class", "x axis")
+				.attr("transform", "translate(0," + height + ")")
+				.call(xAxis);
+	
+		svg.append("g")
+				.attr("class", "y axis")
+				.call(yAxis)
+				.append("text")
+				.attr("transform", "rotate(-90)")
+				.attr("y", 6)
+				.attr("dy", ".71em")
+				.style("text-anchor", "end")
+				.text("Duration");
+	
+		svg.append("path")
+				.datum(data)
+				.attr("class", "line")
+				.attr("d", line);
+				
+		var node = svg.selectAll("g.node")
+				.data(data)
+				.attr("class", "node")
+				.enter().append("g")
+				.attr("transform",  function(d) {
+			return "translate(" + x(d.startTime) + "," + y(d.endTime-d.startTime) + ")";
+		});
+		
+		node.append("circle")
+				.attr("r", 5)
+				.attr("class", function(d) {return d.status;})
+				.append("svg:title")
+				.text(function(d) {
+			return d.execId + ":" + d.flowId + " ran in " + getDuration(d.startTime, d.endTime);
+		});
+	}
+});