azkaban-uncached
Changes
build.xml 15(+15 -0)
src/tl/flowsummary.tl 70(+70 -0)
src/web/js/azkaban.flow.view.js 210(+130 -80)
Details
build.xml 15(+15 -0)
diff --git a/build.xml b/build.xml
index 86db40b..7287178 100644
--- a/build.xml
+++ b/build.xml
@@ -61,6 +61,21 @@
<src path="${java.src.dir}" />
<classpath refid="main.classpath" />
</javac>
+
+ <!-- Compile dustjs templates -->
+ <!-- Note: Because apply does not support multiple srcfile and targetfile
+ elements, and for and foreach requires ant-contrib, we use targetfile
+ for the template name parameter and then redirect the output of dustc
+ to the final output file -->
+ <apply dir="${java.src.dir}/tl" executable="dustc" relative="true">
+ <mapper type="glob" from="*.tl" to="*" />
+ <targetfile prefix="--name=" />
+ <srcfile />
+ <fileset dir="${java.src.dir}/tl" includes="*.tl" />
+ <redirector>
+ <outputmapper id="out" type="glob" from="*.tl" to="${dist.dust.dir}/*.js" />
+ </redirector>
+ </apply>
</target>
<target name="jars" depends="build" description="Create azkaban jar">
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index cde432c..3e60e3f 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -17,7 +17,7 @@
<!DOCTYPE html>
<html>
<head>
-#parse( "azkaban/webapp/servlet/velocity/style.vm" )
+#parse("azkaban/webapp/servlet/velocity/style.vm")
<script type="text/javascript" src="${context}/js/jquery/jquery-1.9.1.js"></script>
<script type="text/javascript" src="${context}/js/jqueryui/jquery-ui-1.10.1.custom.js"></script>
@@ -50,18 +50,21 @@
<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-1.10.1.custom.css" />
</head>
<body>
+
#set($current_page="all")
-#parse( "azkaban/webapp/servlet/velocity/nav.vm" )
+#parse("azkaban/webapp/servlet/velocity/nav.vm")
+
<div class="messaging"><p id="messageClose">X</p><p id="message"></p></div>
<div class="content">
+
#if($errorMsg)
<div class="box-error-message">$errorMsg</div>
#else
-#if($error_message != "null")
+ #if($error_message != "null")
<div class="box-error-message">$error_message</div>
-#elseif($success_message != "null")
+ #elseif($success_message != "null")
<div class="box-success-message">$success_message</div>
-#end
+ #end
<div id="all-jobs-content">
<div class="section-hd flow-header">
@@ -80,8 +83,13 @@
<li><a id="graphViewLink" href="#graph">Graph</a></li>
<li class="lidivider">|</li>
<li><a id="executionsViewLink" href="#executions">Executions</a></li>
+ <li class="lidivider">|</li>
+ <li><a id="summaryViewLink" href="#summary">Summary</a></li>
</ul>
</div>
+
+ ## Graph view.
+
<div id="graphView">
<div class="relative">
<div id="jobList" class="jobList">
@@ -97,7 +105,10 @@
</svg>
</div>
</div>
- </div>
+ </div><!-- /#graphView -->
+
+ ## Executions view.
+
<div id="executionsView">
<div id="executionDiv" class="all-jobs executionInfo">
<table id="execTable">
@@ -129,25 +140,31 @@
</ul>
</div>
</div>
- </div>
- <!-- modal content -->
+ </div><!-- /#executionsView -->
+
+ ## Summary view.
+
+ <div id="summaryView">
+ </div><!-- /#summaryView -->
+
+ ## Invalid session modal.
<div id="invalid-session" class="modal">
<h3>Invalid Session</h3>
- <p>Session has expired. Please re-login.</p>
- <div class="actions">
- <a class="yes btn3" id="login-btn" href="#">Re-login</a>
- </div>
+ <p>Session has expired. Please re-login.</p>
+ <div class="actions">
+ <a class="yes btn3" id="login-btn" href="#">Re-login</a>
+ </div>
</div>
-#parse( "azkaban/webapp/servlet/velocity/flowexecutionpanel.vm" )
+ #parse("azkaban/webapp/servlet/velocity/flowexecutionpanel.vm")
#end
</div>
<div id="contextMenu">
</div>
- #parse( "azkaban/webapp/servlet/velocity/messagedialog.vm" )
+#parse("azkaban/webapp/servlet/velocity/messagedialog.vm")
</body>
</html>
src/tl/flowsummary.tl 70(+70 -0)
diff --git a/src/tl/flowsummary.tl b/src/tl/flowsummary.tl
new file mode 100644
index 0000000..b9536c5
--- /dev/null
+++ b/src/tl/flowsummary.tl
@@ -0,0 +1,70 @@
+ <h4>General</h4>
+ <table>
+ <tbody>
+ <tr>
+ <td>Workflow name</td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Workflow Purpose/Description</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Project name</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Name of scheduled item</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Workflow Hadoop User Name</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Responsible team</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Responsible manager</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Responsible Developer</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Hadoop APIs Used</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Additional Hadoop APIs Used</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Streaming Languages Used</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Code Repository</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Does this workflow perform any second, third, or higher level connection graph calculations?</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Can this workflow be run with an empty <strong>/jobs</strong> directory</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>If not, why not?</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>How does the result get to front-end servers?</td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
src/web/js/azkaban.flow.view.js 210(+130 -80)
diff --git a/src/web/js/azkaban.flow.view.js b/src/web/js/azkaban.flow.view.js
index ce01dbc..dfa26bf 100644
--- a/src/web/js/azkaban.flow.view.js
+++ b/src/web/js/azkaban.flow.view.js
@@ -31,22 +31,25 @@ var statusStringMap = {
var handleJobMenuClick = function(action, el, pos) {
var jobid = el[0].jobid;
- var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowId + "&job=" + jobid;
+ var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" +
+ flowId + "&job=" + jobid;
if (action == "open") {
window.location.href = requestURL;
}
- else if(action == "openwindow") {
+ else if (action == "openwindow") {
window.open(requestURL);
}
}
var flowTabView;
azkaban.FlowTabView= Backbone.View.extend({
- events : {
- "click #graphViewLink" : "handleGraphLinkClick",
- "click #executionsViewLink" : "handleExecutionLinkClick"
+ events: {
+ "click #graphViewLink": "handleGraphLinkClick",
+ "click #executionsViewLink": "handleExecutionLinkClick",
+ "click #summaryViewLink": "handleSummaryLinkClick"
},
- initialize : function(settings) {
+
+ initialize: function(settings) {
var selectedView = settings.selectedView;
if (selectedView == "executions") {
this.handleExecutionLinkClick();
@@ -54,26 +57,39 @@ azkaban.FlowTabView= Backbone.View.extend({
else {
this.handleGraphLinkClick();
}
-
},
+
render: function() {
console.log("render graph");
},
+
handleGraphLinkClick: function(){
$("#executionsViewLink").removeClass("selected");
$("#graphViewLink").addClass("selected");
-
+ $('#summaryViewLink').removeClass('selected');
$("#executionsView").hide();
$("#graphView").show();
+ $('#summaryView').hide();
},
+
handleExecutionLinkClick: function() {
$("#graphViewLink").removeClass("selected");
$("#executionsViewLink").addClass("selected");
-
- $("#graphView").hide();
- $("#executionsView").show();
- executionModel.trigger("change:view");
- }
+ $('#summaryViewLink').removeClass('selected');
+ $("#graphView").hide();
+ $("#executionsView").show();
+ $('#summaryView').hide();
+ executionModel.trigger("change:view");
+ },
+
+ handleSummaryLinkClick: function() {
+ $('#graphViewLink').removeClass('selected');
+ $('#executionsViewLink').removeClass('selected');
+ $('#summaryViewLink').addClass('selected');
+ $('#graphView').hide();
+ $('#executionsView').hide();
+ $('#summaryView').show();
+ },
});
var jobListView;
@@ -84,12 +100,14 @@ azkaban.ExecutionsView = Backbone.View.extend({
events: {
"click #pageSelection li": "handleChangePageSelection"
},
+
initialize: function(settings) {
this.model.bind('change:view', this.handleChangeView, this);
this.model.bind('render', this.render, this);
this.model.set({page: 1, pageSize: 16});
this.model.bind('change:page', this.handlePageChange, this);
},
+
render: function(evt) {
console.log("render");
// Render page selections
@@ -155,6 +173,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
this.renderPagination(evt);
},
+
renderPagination: function(evt) {
var total = this.model.get("total");
total = total? total : 1;
@@ -228,6 +247,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
a.attr("href", "#page" + tpage);
}
},
+
handleChangePageSelection: function(evt) {
if ($(evt.currentTarget).hasClass("disabled")) {
return;
@@ -236,6 +256,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
this.model.set({"page": page});
},
+
handleChangeView: function(evt) {
if (this.init) {
return;
@@ -245,32 +266,50 @@ azkaban.ExecutionsView = Backbone.View.extend({
this.handlePageChange(evt);
this.init = true;
},
+
handlePageChange: function(evt) {
var page = this.model.get("page") - 1;
var pageSize = this.model.get("pageSize");
var requestURL = contextURL + "/manager";
var model = this.model;
- $.get(
- requestURL,
- {"project": projectName, "flow":flowId, "ajax": "fetchFlowExecutions", "start":page * pageSize, "length": pageSize},
- function(data) {
- model.set({"executions": data.executions, "total": data.total});
- model.trigger("render");
- },
- "json"
- );
+ var requestData = {
+ "project": projectName,
+ "flow": flowId,
+ "ajax": "fetchFlowExecutions",
+ "start": page * pageSize,
+ "length": pageSize
+ };
+ var successHandler = function(data) {
+ model.set({"executions": data.executions, "total": data.total});
+ model.trigger("render");
+ };
+ $.get(requestURL, requestData, successHandler, "json");
+ }
+});
+
+var summaryView;
+azkaban.SummaryView = Backbone.View.extend({
+ events: {
+ },
+
+ initialize: function(settings) {
+ },
+
+ render: function(evt) {
+
}
});
var exNodeClickCallback = function(event) {
console.log("Node clicked callback");
var jobId = event.currentTarget.jobid;
- var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowId + "&job=" + jobId;
+ var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" +
+ flowId + "&job=" + jobId;
var menu = [
- {title: "Open Job...", callback: function() {window.location.href=requestURL;}},
- {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
+ {title: "Open Job...", callback: function() {window.location.href=requestURL;}},
+ {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
];
contextMenuView.show(event, menu);
@@ -279,11 +318,12 @@ var exNodeClickCallback = function(event) {
var exJobClickCallback = function(event) {
console.log("Node clicked callback");
var jobId = event.currentTarget.jobid;
- var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowId + "&job=" + jobId;
+ var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" +
+ flowId + "&job=" + jobId;
var menu = [
- {title: "Open Job...", callback: function() {window.location.href=requestURL;}},
- {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
+ {title: "Open Job...", callback: function() {window.location.href=requestURL;}},
+ {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
];
contextMenuView.show(event, menu);
@@ -318,14 +358,23 @@ $(function() {
var selected;
// Execution model has to be created before the window switches the tabs.
executionModel = new azkaban.ExecutionModel();
- executionsView = new azkaban.ExecutionsView({el:$('#executionsView'), model: executionModel});
- flowTabView = new azkaban.FlowTabView({el:$( '#headertabs'), selectedView: selected });
+ executionsView = new azkaban.ExecutionsView({
+ el: $('#executionsView'),
+ model: executionModel
+ });
+ summaryView = new azkaban.SummaryView({
+ el: $('#summaryView'),
+ });
+ flowTabView = new azkaban.FlowTabView({
+ el: $('#headertabs'),
+ selectedView: selected
+ });
graphModel = new azkaban.GraphModel();
mainSvgGraphView = new azkaban.SvgGraphView({
el: $('#svgDiv'),
model: graphModel,
- rightClick: {
+ rightClick: {
"node": exNodeClickCallback,
"edge": exEdgeClickCallback,
"graph": exGraphClickCallback
@@ -352,61 +401,62 @@ $(function() {
flowExecuteDialogView.show(executingData);
});
- $.get(
- requestURL,
- {"project": projectName, "ajax":"fetchflowgraph", "flow":flowId},
- function(data) {
- // Create the nodes
- var nodes = {};
- for (var i=0; i < data.nodes.length; ++i) {
- var node = data.nodes[i];
- nodes[node.id] = node;
+ var requestData = {
+ "project": projectName,
+ "ajax": "fetchflowgraph",
+ "flow":flowId
+ };
+ var successHandler = function(data) {
+ // Create the nodes
+ var nodes = {};
+ for (var i = 0; i < data.nodes.length; ++i) {
+ var node = data.nodes[i];
+ nodes[node.id] = node;
+ }
+ for (var i = 0; i < data.edges.length; ++i) {
+ var edge = data.edges[i];
+ var fromNode = nodes[edge.from];
+ var toNode = nodes[edge.target];
+
+ if (!fromNode.outNodes) {
+ fromNode.outNodes = {};
}
- for (var i=0; i < data.edges.length; ++i) {
- var edge = data.edges[i];
- var fromNode = nodes[edge.from];
- var toNode = nodes[edge.target];
-
- if (!fromNode.outNodes) {
- fromNode.outNodes = {};
- }
- fromNode.outNodes[toNode.id] = toNode;
-
- if (!toNode.inNodes) {
- toNode.inNodes = {};
- }
- toNode.inNodes[fromNode.id] = fromNode;
+ fromNode.outNodes[toNode.id] = toNode;
+
+ if (!toNode.inNodes) {
+ toNode.inNodes = {};
}
+ toNode.inNodes[fromNode.id] = fromNode;
+ }
+
+ console.log("data fetched");
+ graphModel.set({data: data});
+ graphModel.set({nodes: nodes});
+ graphModel.set({disabled: {}});
+ graphModel.trigger("change:graph");
- console.log("data fetched");
- graphModel.set({data: data});
- graphModel.set({nodes: nodes});
- graphModel.set({disabled: {}});
- graphModel.trigger("change:graph");
-
- // Handle the hash changes here so the graph finishes rendering first.
- if (window.location.hash) {
- var hash = window.location.hash;
- if (hash == "#executions") {
+ // Handle the hash changes here so the graph finishes rendering first.
+ if (window.location.hash) {
+ var hash = window.location.hash;
+ if (hash == "#executions") {
+ flowTabView.handleExecutionLinkClick();
+ }
+ else if (hash == "#graph") {
+ // Redundant, but we may want to change the default.
+ selected = "graph";
+ }
+ else {
+ if ("#page" == hash.substring(0, "#page".length)) {
+ var page = hash.substring("#page".length, hash.length);
+ console.log("page " + page);
flowTabView.handleExecutionLinkClick();
- }
- else if (hash == "#graph") {
- // Redundant, but we may want to change the default.
- selected = "graph";
+ executionModel.set({"page": parseInt(page)});
}
else {
- if ("#page" == hash.substring(0, "#page".length)) {
- var page = hash.substring("#page".length, hash.length);
- console.log("page " + page);
- flowTabView.handleExecutionLinkClick();
- executionModel.set({"page": parseInt(page)});
- }
- else {
- selected = "graph";
- }
+ selected = "graph";
}
}
- },
- "json"
- );
+ }
+ };
+ $.get(requestURL, requestData, successHandler, "json");
});