azkaban-uncached

Finish initial flow summary UI.

12/4/2013 4:16:35 PM

Details

build.xml 5(+5 -0)

diff --git a/build.xml b/build.xml
index 2a5ecd4..7b1479b 100644
--- a/build.xml
+++ b/build.xml
@@ -265,6 +265,11 @@
 			<fileset dir="${web.src.dir}" />
 		</copy>
 		
+		<!-- Copy compiled dust templates -->
+		<copy todir="${dist.solo.package.dir}/web/js">
+			<fileset dir="${dist.dust.dir}" />
+		</copy>
+		
 		<!-- Copy sql files -->
 		<copy todir="${dist.solo.package.dir}/sql" >
 			<fileset dir="${sql.src.dir}" />
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index 1f7de6a..9916015 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -27,6 +27,7 @@
 		<script type="text/javascript" src="${context}/js/jquery.simplemodal-1.4.4.js"></script>
 		
 		<script type="text/javascript" src="${context}/js/dust-core-2.2.2.min.js"></script>
+		<script type="text/javascript" src="${context}/js/flowsummary.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban.date.utils.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban.ajax.utils.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban.common.utils.js"></script>
@@ -48,6 +49,11 @@
 			var flowId = "${flowid}";
 			var execId = null;
 		</script>
+		<style>
+			.worksheet-key {
+				width: 25%;
+			}
+		</style>
 		<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-1.10.1.custom.css" />
 	</head>
 	<body>
@@ -140,13 +146,14 @@
 								<li id="next"><a>Next<span class="arrow">&rarr;</span></a></li>
 							</ul>
 						</div>
-					</div>
-				</div><!-- /#executionsView -->
-
+					</div><!-- /#executionsView -->
+	
 	## Summary view.
 
-				<div id="summaryView">
-				</div><!-- /#summaryView -->
+					<div id="summaryView">
+					</div><!-- /#summaryView -->
+
+				</div>
 
 	## Invalid session modal.
 

src/tl/flowsummary.tl 138(+94 -44)

diff --git a/src/tl/flowsummary.tl b/src/tl/flowsummary.tl
index c3fa367..0014d01 100644
--- a/src/tl/flowsummary.tl
+++ b/src/tl/flowsummary.tl
@@ -1,155 +1,205 @@
+					<br />
 					<h4>General</h4>
 					<table>
 						<tbody>
 							<tr>
-								<td>Workflow name</td>
-								<td>{general.flowName}</td>
+								<td class="worksheet-key">Workflow name</td>
+								<td>{general.flowId}</td>
 							</tr>
-							<tr>
-								<td>Workflow Purpose/Description</td>
-								<td>{general.flowDescription}</td>
+							<tr class="editRow">
+								<td class="worksheet-key">Workflow Purpose/Description</td>
+								<td class="editable"><span class="spanValue">{general.flowDescription}</span></td>
 							</tr>
 							<tr>
-								<td>Project name</td>
-								<td>{general.project}</td>
+								<td class="worksheet-key">Project name</td>
+								<td>{general.projectName}</td>
 							</tr>
 							<tr>
-								<td>Name of scheduled item</td>
+								<td class="worksheet-key">Name of scheduled item</td>
 								<td>{general.flowId}</td>
 							</tr>
 							<tr>
-								<td>Workflow Hadoop User Name</td>
+								<td class="worksheet-key">Workflow Hadoop User Name</td>
 								<td>{general.user}</td>
 							</tr>
 							<tr>
-								<td>Responsible team</td>
-								<td>{general.team}</td>
+								<td class="worksheet-key">Responsible team</td>
+								<td class="editable"><span class="spanValue">{general.team}</span></td>
 							</tr>
 							<tr>
-								<td>Responsible manager</td>
-								<td>{general.manager}</td>
+								<td class="worksheet-key">Responsible manager</td>
+								<td class="editable"><span class="spanValue">{general.manager}</span></td>
 							</tr>
 							<tr>
-								<td>Responsible Developer</td>
-								<td>{general.developer}</td>
+								<td class="worksheet-key">Responsible Developer</td>
+								<td class="editable"><span class="spanValue">{general.developer}</span></td>
 							</tr>
 							<tr>
-								<td>Hadoop APIs Used</td>
+								<td class="worksheet-key">Hadoop APIs Used</td>
 								<td>{general.apis}</td>
 							</tr>
 							<tr>
-								<td>Additional Hadoop APIs Used</td>
+								<td class="worksheet-key">Additional Hadoop APIs Used</td>
 								<td>{general.additionalApis}</td>
 							</tr>
 							<tr>
-								<td>Streaming Languages Used</td>
+								<td class="worksheet-key">Streaming Languages Used</td>
 								<td>{general.streamingLanguages}</td>
 							</tr>
 							<tr>
-								<td>Code Repository</td>
+								<td class="worksheet-key">Code Repository</td>
 								<td>{general.codeRepository}</td>
 							</tr>
 							<tr>
-								<td>Does this workflow perform any second, third, or higher level connection graph calculations?</td>
+								<td class="worksheet-key">Does this workflow perform any second, third, or higher level connection graph calculations?</td>
 								<td>{general.higherLevelGraphOperations}</td>
 							</tr>
 							<tr>
-								<td>Can this workflow be run with an empty <strong>/jobs</strong> directory</td>
+								<td class="worksheet-key">Can this workflow be run with an empty <strong>/jobs</strong> directory</td>
 								<td>{general.emptyJobsDirectory}</td>
 							</tr>
 							<tr>
-								<td>If not, why not?</td>
+								<td class="worksheet-key">If not, why not?</td>
 								<td>{general.emptyJobsDirectoryReason}</td>
 							</tr>
 							<tr>
-								<td>How does the result get to front-end servers?</td>
+								<td class="worksheet-key">How does the result get to front-end servers?</td>
 								<td>{general.toFrontEnd}</td>
 							</tr>
 						</tbody>
 					</table>
 
+					<br />
 					<h4>Scheduling</h4>
 					<table>
 						<tbody>
 							<tr>
-								<td>Max Map Slots from Largest Job</td>
+								<td class="worksheet-key">Max Map Slots from Largest Job</td>
 								<td>{scheduling.maxMapSlots}</td>
 							</tr>
 							<tr>
-								<td>Max Reduce Slots from Largest Job</td>
+								<td class="worksheet-key">Max Reduce Slots from Largest Job</td>
 								<td>{scheduling.maxReduceSlots}</td>
 							</tr>
 							<tr>
-								<td>Total Reduce Slots from All Jobs</td>
+								<td class="worksheet-key">Total Reduce Slots from All Jobs</td>
 								<td>{scheduling.totalReduceSlots}</td>
 							</tr>
 							<tr>
-								<td>Total Number of Jobs</td>
+								<td class="worksheet-key">Total Number of Jobs</td>
 								<td>{scheduling.numJobs}</td>
 							</tr>
 							<tr>
-								<td>Longest Task Time</td>
+								<td class="worksheet-key">Longest Task Time</td>
 								<td>{scheduling.longestTaskTime}</td>
 							</tr>
 							<tr>
-								<td>Required Schedule</td>
+								<td class="worksheet-key">Required Schedule</td>
 								<td>{scheduling.schedule}</td>
 							</tr>
 							<tr>
-								<td>Launch Time</td>
+								<td class="worksheet-key">Launch Time</td>
 								<td>{scheduling.launchTime}</td>
 							</tr>
 							<tr>
-								<td>Total Workflow Run Time (hours)</td>
+								<td class="worksheet-key">Total Workflow Run Time (hours)</td>
 								<td>{scheduling.totalFlowTime}</td>
 							</tr>
 							<tr>
-								<td>Expected Time of Completion</td>
+								<td class="worksheet-key">Expected Time of Completion</td>
 								<td>{scheduling.expectedCompletionTime}</td>
 							</tr>
 							<tr>
-								<td>Max Permitted Delay</td>
+								<td class="worksheet-key">Max Permitted Delay</td>
 								<td>{scheduling.maxPermittedDelay}</td>
 							</tr>
 						</tbody>
 					</table>
 
+					<br />
 					<h4>Resources</h4>
 					<table>
 						<tbody>
 							<tr>
-								<td>Size of largest <code>-Xmx</code> value?</td>
+								<td class="worksheet-key">Is any of the code specifically multi-threaded?</td>
+								<td colspan="3">{resources.multithreaded}</td>
+							</tr>
+							<tr>
+								<td class="worksheet-key">Do you 'fat jar' any hadoop-core jars?</td>
+								<td colspan="3">{resources.fatJar}</td>
+							</tr>
+							<tr>
+								<td class="worksheet-key">Which job has the largest spill count?</td>
+								<td>{resources.largestSpill.job}</td>
+								<td class="worksheet-key">Largest spill count for any given task?</td>
+								<td>{resources.largestSpill.count}</td>
+							</tr>
+							<tr>
+								<td class="worksheet-key">Is there a distributed cache in use?</td>
+								<td>{resources.distributedCache.using}</td>
+								<td class="worksheet-key">How big is the distributed cache?</td>
+								<td>{resources.distributedCache.size}</td>
+							</tr>
+							<tr>
+								<td class="worksheet-key">Size of largest <code>-Xmx</code> value?</td>
 								<td>{resources.largestXmx.size}</td>
-								<td>If this is above 1G, please explain why</td>
+								<td class="worksheet-key">If this is above 1G, please explain why</td>
 								<td>{resources.largestXmx.reason}</td>
 							</tr>
 							<tr>
-								<td>Do any jobs use <code>-Xms</code>?</td>
+								<td class="worksheet-key">Do any jobs use <code>-Xms</code>?</td>
 								<td>{resources.xms.using}</td>
-								<td>If so, why?</td>
+								<td class="worksheet-key">If so, why?</td>
 								<td>{resources.xms.reason}</td>
 							</tr>
 							<tr>
-								<td>Is <em>intermediate compression</em> specifically turned on?</td>
+								<td class="worksheet-key">Is <em>intermediate compression</em> specifically turned on?</td>
 								<td>{resources.intermediateCompression.on}</td>
-								<td>If so, which codec</td>
+								<td class="worksheet-key">If so, which codec</td>
 								<td>{resources.intermediateCompression.codec}</td>
 							</tr>
 							<tr>
-								<td>Are there combiners in use?</td>
+								<td class="worksheet-key">Are there combiners in use?</td>
 								<td colspan="3">{resources.combiners}</td>
 							</tr>
 							<tr>
-								<td>Size of largest <code>mapred.job.map.memory.mb</code></td>
+								<td class="worksheet-key">Size of largest <code>mapred.job.map.memory.mb</code></td>
 								<td>{resources.largestMapredJobMapMemoryMb.size}</td>
-								<td>Used by job</td>
+								<td class="worksheet-key">Used by job</td>
 								<td>{resources.largestMapredJobMapMemoryMb.job}</td>
 							</tr>
 							<tr>
-								<td>Size of largest <code>mapred.job.reduce.memory.mb</code></td>
+								<td class="worksheet-key">Size of largest <code>mapred.job.reduce.memory.mb</code></td>
 								<td>{resources.largestMapredJobReduceMemoryMb.size}</td>
-								<td>Used by job</td>
+								<td class="worksheet-key">Used by job</td>
 								<td>{resources.largestMapredJobMapMemoryMb.job}</td>
 							</tr>
 						</tbody>
 					</table>
+
+					<br />
+					<h4>Input/Output</h4>
+					<table>
+						<tbody>
+							<tr>
+								<td class="worksheet-key">List of input HDFS file paths</td>
+								<td>{io.hdfsPaths}</td>
+							</tr>
+							<tr>
+								<td class="worksheet-key">Number of files generated (hadoop dfs -count)</td>
+								<td>{io.hdfsFileCount}</td>
+							</tr>
+							<tr>
+								<td class="worksheet-key">Average size of files generated in GB</td>
+								<td>{io.averageFileSize}</td>
+							</tr>
+							<tr>
+								<td class="worksheet-key">Size of intermediate data (content of /jobs - output going to production) in GB on HDFS</td>
+								<td>{io.intermediateFileSize}</td>
+							</tr>
+							<tr>
+								<td class="worksheet-key">Size of final output data in GB on HDFS</td>
+								<td>{io.finalOutputSize}</td>
+							</tr>
+						</tbody>
+					</table>
diff --git a/src/web/js/azkaban.flow.view.js b/src/web/js/azkaban.flow.view.js
index dfa26bf..043fb22 100644
--- a/src/web/js/azkaban.flow.view.js
+++ b/src/web/js/azkaban.flow.view.js
@@ -42,7 +42,7 @@ var handleJobMenuClick = function(action, el, pos) {
 }
 
 var flowTabView;
-azkaban.FlowTabView= Backbone.View.extend({
+azkaban.FlowTabView = Backbone.View.extend({
 	events: {
 		"click #graphViewLink": "handleGraphLinkClick",
 		"click #executionsViewLink": "handleExecutionLinkClick",
@@ -253,7 +253,6 @@ azkaban.ExecutionsView = Backbone.View.extend({
 			return;
 		}
 		var page = evt.currentTarget.page;
-		
 		this.model.set({"page": page});
 	},
 	
@@ -261,7 +260,6 @@ azkaban.ExecutionsView = Backbone.View.extend({
 		if (this.init) {
 			return;
 		}
-		
 		console.log("init");
 		this.handlePageChange(evt);
 		this.init = true;
@@ -291,14 +289,98 @@ azkaban.ExecutionsView = Backbone.View.extend({
 var summaryView;
 azkaban.SummaryView = Backbone.View.extend({
 	events: {
+		"click": "closeEditingTarget",
+		"click table .editable": "handleEditField"
 	},
 	
 	initialize: function(settings) {
+		console.log("summaryView initialize");
+		var general = {
+			flowName: "",
+			flowDescription: "",
+			projectName: projectName,
+			flowId: flowId
+		};
+
+		var scheduling = {};
+		var resources = {};
+		var io = {};
+
+		this.model.bind('change:view', this.handleChangeView, this);
+		this.model.bind('render', this.render, this);
+		this.model.set({
+			'general': general,
+			'scheduling': scheduling,
+			'resources': resources,
+			'io': io
+		});
+		this.model.trigger('render');
+	},
+
+	handleChangeView: function(evt) {
+		console.log("summaryView handleChangeView");
+	},
+
+	handleEditField: function(evt) {
+		var curTarget = evt.currentTarget;
+		console.log("summaryView handleEditField");
+		if (this.editingTarget != curTarget) {
+			this.closeEditingTarget(evt);
+
+			var text = $(curTarget).children('.spanValue').text();
+			$(curTarget).empty();
+
+			var input = document.createElement('input');
+			$(input).attr('type', 'text');
+			$(input).css('width', '100%');
+			$(input).val(text);
+
+			$(curTarget).addClass('editing');
+			$(curTarget).append(input);
+			$(input).focus();
+			var obj = this;
+			$(input).keypress(function(evt) {
+				if (evt.which == 13) {
+					obj.closeEditingTarget(evt);
+				}
+			});
+			this.editingTarget = curTarget;
+		}
+		evt.preventDefault();
+		evt.stopPropagation();
+	},
+
+	closeEditingTarget: function(evt) {
+		console.log("summaryView closeEditingTarget");
+		if (this.editingTarget != null &&
+				this.editingTarget != evt.target &&
+				this.editingTarget != evt.target.myparent) {
+			var input = $(this.editingTarget).children("input")[0];
+			var text = $(input).val();
+			$(input).remove();
+
+			var valueData = document.createElement("span");
+			$(valueData).addClass("spanValue");
+			$(valueData).text(text);
+
+			$(this.editingTarget).removeClass("editing");
+			$(this.editingTarget).append(valueData);
+			valueData.myparent = this.editingTarget;
+			this.editingTarget = null;
+		}
 	},
 	
 	render: function(evt) {
-
-	}
+		console.log("summaryView render");
+		var data = {
+			general: this.model.get('general'),
+			scheduling: this.model.get('scheduling'),
+			resources: this.model.get('resources')
+		};
+		dust.render("flowsummary", data, function(err, out) {
+			$('#summaryView').html(out);
+		});
+	},
 });
 
 var exNodeClickCallback = function(event) {
@@ -352,6 +434,10 @@ azkaban.GraphModel = Backbone.Model.extend({});
 
 var executionModel;
 azkaban.ExecutionModel = Backbone.Model.extend({});
+
+var summaryModel;
+azkaban.SummaryModel = Backbone.Model.extend({});
+
 var mainSvgGraphView;
 
 $(function() {
@@ -362,8 +448,10 @@ $(function() {
 		el: $('#executionsView'), 
 		model: executionModel
 	});
+	summaryModel = new azkaban.SummaryModel();
 	summaryView = new azkaban.SummaryView({
 		el: $('#summaryView'),
+		model: summaryModel
 	});
 	flowTabView = new azkaban.FlowTabView({
 		el: $('#headertabs'),