azkaban-developers
Changes
src/web/js/azkaban/view/flow.js 8(+8 -0)
src/web/js/azkaban/view/job-history.js 108(+5 -103)
src/web/js/azkaban/view/time-graph.js 125(+125 -0)
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}";
src/web/js/azkaban/view/flow.js 8(+8 -0)
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
src/web/js/azkaban/view/job-history.js 108(+5 -103)
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"
});
});
src/web/js/azkaban/view/time-graph.js 125(+125 -0)
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);
+ });
+ }
+});