Repository: zeppelin
Updated Branches:
  refs/heads/master 3ad809f6b -> ca4e587f1


ZEPPELIN-964 ] Jobmanagement - (2) basic front-end

### What is this PR for?
Job manger basic front end.
You can check to paragraph and notebook information.
It is created with the following additional functions PR.
(E. G., Filter and sort)

this PR is divided from the https://github.com/apache/zeppelin/pull/921

### What type of PR is it?
Featrue

### Todos
- [x] - notebook information
- [x] - viewing status for paragraph
- [x] - feat. running progress bar.
- [x] - added job menu in navbar.
(It can be modified by the following 
(https://github.com/apache/zeppelin/pull/1013)

### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-964

### How should this be tested?
1. create and running, modified notebook and paragraph.
2. check to cron or normal notebook status in job manager.

### Screenshots

<img width="683" alt="job manger-basic" 
src="https://cloud.githubusercontent.com/assets/10525473/16113612/0120dec8-33f8-11e6-8dec-c74048fae637.png";>

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? yes

Author: CloverHearts <[email protected]>
Author: CloverHearts <[email protected]>

This patch had conflicts when merged, resolved by
Committer: Lee moon soo <[email protected]>

Closes #1025 from cloverhearts/dev/jobmanager/step/02-basic-front and squashes 
the following commits:

aa7f502 [CloverHearts] Merge branch 'master' into 
dev/jobmanager/step/02-basic-front
c759cff [CloverHearts] icon spin animation support for chrome.
f183d73 [CloverHearts] job paragraph information tooltip top to top-left
db76838 [CloverHearts] restore location for job menu in navbar
bb8858c [CloverHearts] Merge branch 'master' into 
dev/jobmanager/step/02-basic-front
5d75520 [CloverHearts] Merge branch 'master' into 
dev/jobmanager/step/02-basic-front
0313cfa [CloverHearts] Merge branch 'master' into 
dev/jobmanager/step/02-basic-front
baf3ec6 [CloverHearts] Merge branch 'master' into 
dev/jobmanager/step/02-basic-front
9ee539b [CloverHearts] Merge branch 'master' into 
dev/jobmanager/step/02-basic-front
5d64018 [CloverHearts] Merge branch 'master' into 
dev/jobmanager/step/02-basic-front
75186c7 [CloverHearts] implement basic - frontend for job manager.
18db280 [CloverHearts] modifed get run status for paragraph and bug fixed.


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/ca4e587f
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/ca4e587f
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/ca4e587f

Branch: refs/heads/master
Commit: ca4e587f11a90169b7ae95fe53ce909b5226cf1a
Parents: 3ad809f
Author: CloverHearts <[email protected]>
Authored: Tue Jul 5 12:45:28 2016 +0900
Committer: Lee moon soo <[email protected]>
Committed: Thu Jul 7 08:38:12 2016 -0700

----------------------------------------------------------------------
 zeppelin-web/src/app/app.js                     |   8 +-
 .../src/app/jobmanager/jobmanager.controller.js |  93 +++++++++++++++
 zeppelin-web/src/app/jobmanager/jobmanager.css  |  43 +++++++
 zeppelin-web/src/app/jobmanager/jobmanager.html | 119 +++++++++++++++++++
 .../src/app/jobmanager/jobs/job-control.html    |  31 +++++
 .../app/jobmanager/jobs/job-progressBar.html    |  22 ++++
 .../src/app/jobmanager/jobs/job.controller.js   |  39 ++++++
 zeppelin-web/src/app/jobmanager/jobs/job.css    | 118 ++++++++++++++++++
 zeppelin-web/src/app/jobmanager/jobs/job.html   | 107 +++++++++++++++++
 zeppelin-web/src/components/navbar/navbar.html  |   1 +
 .../websocketEvents/websocketEvents.factory.js  |   4 +
 .../websocketEvents/websocketMsg.service.js     |  14 +++
 zeppelin-web/src/index.html                     |   4 +
 .../org/apache/zeppelin/notebook/Notebook.java  |   4 +-
 14 files changed, 603 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/app.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js
index bc8e52c..b5f6c85 100644
--- a/zeppelin-web/src/app/app.js
+++ b/zeppelin-web/src/app/app.js
@@ -62,6 +62,10 @@
                     templateUrl: 'app/notebook/notebook.html',
                     controller: 'NotebookCtrl'
                 })
+                .when('/jobmanager', {
+                    templateUrl: 'app/jobmanager/jobmanager.html',
+                    controller: 'JobmanagerCtrl'
+                })
                 .when('/interpreter', {
                     templateUrl: 'app/interpreter/interpreter.html',
                     controller: 'InterpreterCtrl'
@@ -71,8 +75,8 @@
                     controller: 'CredentialCtrl'
                 })
                 .when('/configuration', {
-                  templateUrl: 'app/configuration/configuration.html',
-                  controller: 'ConfigurationCtrl'
+                    templateUrl: 'app/configuration/configuration.html',
+                    controller: 'ConfigurationCtrl'
                 })
                 .when('/search/:searchTerm', {
                     templateUrl: 'app/search/result-list.html',

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/jobmanager/jobmanager.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobmanager.controller.js 
b/zeppelin-web/src/app/jobmanager/jobmanager.controller.js
new file mode 100644
index 0000000..760be45
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobmanager.controller.js
@@ -0,0 +1,93 @@
+/*jshint loopfunc: true, unused:false */
+/*
+ * 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.
+ */
+'use strict';
+
+angular.module('zeppelinWebApp')
+  .controller('JobmanagerCtrl',
+    function($scope, $route, $routeParams, $location, $rootScope, $http, $q,
+             websocketMsgSrv, baseUrlSrv, $interval, $timeout, SaveAsService) {
+
+      $scope.$on('setNotebookJobs', function(event, responseData) {
+        $scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
+        $scope.jobInfomations = responseData.jobs;
+        $scope.jobInfomationsIndexs = $scope.jobInfomations? 
_.indexBy($scope.jobInfomations, 'notebookId') : {};
+      });
+
+      $scope.$on('setUpdateNotebookJobs', function(event, responseData) {
+        var jobInfomations = $scope.jobInfomations;
+        var indexStore = $scope.jobInfomationsIndexs;
+        $scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
+        var notes = responseData.jobs;
+        notes.map(function (changedItem) {
+          if (indexStore[changedItem.notebookId] === undefined) {
+            var newItem = angular.copy(changedItem);
+            jobInfomations.push(newItem);
+            indexStore[changedItem.notebookId] = newItem;
+          } else {
+            var changeOriginTarget = indexStore[changedItem.notebookId];
+
+            if (changedItem.isRemoved !== undefined && changedItem.isRemoved 
=== true) {
+
+              // remove Item.
+              var removeIndex = _.findIndex(indexStore, 
changedItem.notebookId);
+              if (removeIndex > -1) {
+                indexStore.splice(removeIndex, 1);
+              }
+
+              removeIndex = _.findIndex(jobInfomations, { 'notebookId' : 
changedItem.notebookId});
+              if (removeIndex) {
+                jobInfomations.splice(removeIndex, 1);
+              }
+
+            } else {
+              // change value for item.
+              changeOriginTarget.isRunningJob = changedItem.isRunningJob;
+              changeOriginTarget.notebookName = changedItem.notebookName;
+              changeOriginTarget.notebookType = changedItem.notebookType;
+              changeOriginTarget.interpreter = changedItem.interpreter;
+              changeOriginTarget.unixTimeLastRun = changedItem.unixTimeLastRun;
+              changeOriginTarget.paragraphs = changedItem.paragraphs;
+            }
+          }
+        });
+      });
+
+      $scope.filterValueToName = function (filterValue) {
+        var index = _.findIndex($scope.ACTIVE_INTERPRETERS, {value : 
filterValue});
+
+        if ($scope.ACTIVE_INTERPRETERS[index].name !== undefined) {
+          return $scope.ACTIVE_INTERPRETERS[index].name;
+        } else {
+          return 'undefined';
+        }
+      };
+
+      $scope.init = function () {
+        $scope.jobInfomations = [];
+        $scope.JobInfomationsByFilter = $scope.jobInfomations;
+
+        websocketMsgSrv.getNotebookJobsList();
+        var refreshObj = $interval(function () {
+          if ($scope.lastJobServerUnixTime !== undefined) {
+            
websocketMsgSrv.getUpdateNotebookJobsList($scope.lastJobServerUnixTime);
+          }
+        }, 1000);
+
+        $scope.$on('$destroy', function() {
+          $interval.cancel(refreshObj);
+          websocketMsgSrv.unsubscribeJobManager();
+        });
+      };
+});

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/jobmanager/jobmanager.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobmanager.css 
b/zeppelin-web/src/app/jobmanager/jobmanager.css
new file mode 100644
index 0000000..0e60d56
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobmanager.css
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+.job-col {
+  margin: 0;
+  padding: 0;
+}
+
+.job {
+  padding: 2px 8px 4px 8px;
+  min-height: 32px;
+}
+
+.jobManagerHead {
+  margin: -10px -10px 20px;
+  padding: 10px 15px 15px 15px;
+  background: white;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
+  border-bottom: 1px solid #E5E5E5;
+}
+
+.jobManagerHead .header {
+  font-family: 'Roboto', sans-serif;
+}
+
+.job-note-name-query {
+  padding: 6px;
+  color: #000;
+  height: 22px;
+  width: 200px;
+  font: normal normal normal 14px/1 FontAwesome;
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/jobmanager/jobmanager.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobmanager.html 
b/zeppelin-web/src/app/jobmanager/jobmanager.html
new file mode 100644
index 0000000..598c3f7
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobmanager.html
@@ -0,0 +1,119 @@
+<!--
+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.
+-->
+<!-- Here the controller <JobmanagerCtrl> is not needed because explicitly set 
in the app.js (route) -->
+<div class="jobManagerHead" data-ng-init="init()">
+  <div class="header">
+    <div class="row">
+      <div class="col-md-12">
+        <h3 class="new_h3">
+          Job
+        </h3>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-12">
+        You can monitor the written notebook. Check the status of the Notebook 
and can control the action.
+      </div>
+    </div>
+  </div>
+  <div style="margin: 0px">
+    <hr style="margin-top: 10px; margin-bottom: 10px;" />
+  </div>
+
+  <div class="row">
+    <div
+      class="col-md-12 text-right"
+      style="padding-top: 6px;">
+      <span
+        ng-repeat="jobStatus in ['READY', 'FINISHED', 'ABORT', 
'ERROR','PENDING','RUNNING']"
+        ng-switch="jobStatus">
+        <span
+          ng-switch-when="FINISHED">
+          <i style="color: green; margin-right: 3px;" class="fa fa-circle"
+             ng-click=""
+             tooltip="FINISHED">
+          </i>
+          {{jobStatus}}
+        </span>
+        <span
+          ng-switch-when="RUNNING"
+          style="margin-right: 3px;">
+          <i style="color: blue" class="fa fa-spinner"
+             ng-click=""
+             tooltip="RUNNING">
+          </i>
+          {{jobStatus}}
+        </span>
+        <span
+          ng-switch-when="READY"
+          style="margin-right: 3px;">
+          <i style="color: green" class="fa fa-circle-o"
+             ng-click=""
+             tooltip="READY">
+          </i>
+          {{jobStatus}}
+        </span>
+        <span
+          ng-switch-when="PENDING"
+          style="margin-right: 3px;">
+          <i style="color: gray" class="fa fa-circle"
+             ng-click=""
+             tooltip="PENDING"
+          >
+          </i>
+          {{jobStatus}}
+        </span>
+        <span
+          ng-switch-when="ABORT"
+          style="margin-right: 3px;">
+          <i style="color: orange" class="fa fa-circle"
+             ng-click=""
+             tooltip="ABORT">
+          </i>
+          {{jobStatus}}
+        </span>
+        <span
+          ng-switch-when="ERROR"
+          style="margin-right: 3px;">
+          <i style="color: red" class="fa fa-circle"
+             ng-click=""
+             tooltip="ERROR">
+          </i>
+          {{jobStatus}}
+        </span>
+      </span>
+    </div>
+  </div>
+</div>
+<div>
+  <div class="note-jump"></div>
+  <div
+    ng-if="jobInfomations.length > 0"
+    ng-repeat="notebookJob in jobInfomations"
+    class="paragraph-col">
+    <div ng-include src="'app/jobmanager/jobs/job.html'"
+         class="job-space box job-margin"
+         ng-controller="JobCtrl">
+    </div>
+  </div>
+  <div
+    ng-if="jobInfomations.length <= 0"
+    class="paragraph-col">
+    <div
+      class="job-space box job-margin text-center">
+      Data does not exist
+    </div>
+  </div>
+  <div style="clear:both;height:10px"></div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/jobmanager/jobs/job-control.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobs/job-control.html 
b/zeppelin-web/src/app/jobmanager/jobs/job-control.html
new file mode 100644
index 0000000..2cd0bf8
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobs/job-control.html
@@ -0,0 +1,31 @@
+<!--
+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.
+-->
+
+<div id="{{notebookJob.notebookId}}_control" class="control">
+  <span>
+    {{lastExecuteTime(notebookJob.unixTimeLastRun)}}
+  </span>
+  <span>
+    <span ng-if="notebookJob.isRunningJob === true">
+      Notebook is RUNNING
+    </span>
+    <span ng-if="notebookJob.isRunningJob === false">
+      Notebook is READY
+    </span>
+  </span>
+
+  <span ng-if="notebookJob.isRunningJob === true">
+    {{getProgress()}}%
+  </span>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/jobmanager/jobs/job-progressBar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobs/job-progressBar.html 
b/zeppelin-web/src/app/jobmanager/jobs/job-progressBar.html
new file mode 100644
index 0000000..11e3c17
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobs/job-progressBar.html
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+
+<div id="{{notebookJob.notebookId}}_runControl" class="runControl">
+  <div id="{{notebookJob.notebookId}}_progress" class="progress" 
ng-if="notebookJob.isRunningJob === true">
+      <div ng-if="getProgress()>0 && getProgress()<100 && 
notebookJob.isRunningJob === true"
+        class="progress-bar" role="progressbar" 
ng-style="{width:getProgress()+'%'}"></div>
+      <div ng-if="(getProgress()<=0 || getProgress()>=100) && 
(notebookJob.isRunningJob === true)"
+          class="progress-bar progress-bar-striped active" role="progressbar" 
style="width:100%;"></div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/jobmanager/jobs/job.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobs/job.controller.js 
b/zeppelin-web/src/app/jobmanager/jobs/job.controller.js
new file mode 100644
index 0000000..dcd470a
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobs/job.controller.js
@@ -0,0 +1,39 @@
+/*jshint loopfunc: true, unused:false */
+/*
+ * 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.
+ */
+'use strict';
+
+angular.module('zeppelinWebApp')
+  .controller('JobCtrl', function($scope,$rootScope, $http, baseUrlSrv) {
+
+    $scope.init = function (jobInformation) {
+      $scope.progressValue = 0;
+    };
+
+    $scope.getProgress = function () {
+      var statusList = _.pluck($scope.notebookJob.paragraphs, 'status');
+      var runningJob = _.countBy(statusList, function (status) {
+        if (status === 'FINISHED' || status === 'RUNNING') {
+          return 'matchCount';
+        } else {
+          return 'none';
+        }
+      });
+      var totalCount = statusList.length;
+      var runningJobCount = runningJob.matchCount;
+      var result = Math.ceil(runningJobCount / totalCount * 100);
+      return isNaN(result)? 0 : result;
+    };
+
+});

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/jobmanager/jobs/job.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobs/job.css 
b/zeppelin-web/src/app/jobmanager/jobs/job.css
new file mode 100644
index 0000000..2bf7a56
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobs/job.css
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+/*
+  job Style
+*/
+
+.job-space {
+  margin-bottom: 5px !important;
+  padding: 10px 10px 10px 10px !important;
+  min-height: 30px;
+}
+
+.job-margin {
+  margin-right: 2px;
+  margin-left: 2px;
+}
+
+.job-types i {
+  font-weight: bold;
+  font-size: 10px;
+}
+
+
+/*
+  job Controls CSS
+*/
+
+.job .runControl {
+  font-size: 1px;
+  color: #AAAAAA;
+  height:4px;
+  margin: 0px 0px 0px 0px;
+}
+
+.job .runControl .progress {
+  position: relative;
+  width: 100%;
+  height: 4px;
+  z-index: 100;
+  border-radius: 0;
+}
+
+.job .control span {
+  margin-left: 4px;
+}
+
+.job .control {
+  background: rgba(255,255,255,0.85);
+  float: right;
+  color: #999;
+  margin-top: 1px;
+  margin-right: 5px;
+  position: absolute;
+  clear: both;
+  right: 15px;
+  text-align: right;
+  font-size: 12px;
+  padding: 4px;
+}
+
+.job .control li {
+  font-size: 12px;
+  margin-bottom: 4px;
+  color: #333333;
+}
+
+.job .control .tooltip {
+  z-index: 10003;
+}
+
+@-webkit-keyframes spinnerRotateAnimation
+{
+  from{-webkit-transform:rotate(0deg);}
+  to{-webkit-transform:rotate(360deg);}
+}
+@-moz-keyframes spinnerRotateAnimation
+{
+  from{-moz-transform:rotate(0deg);}
+  to{-moz-transform:rotate(360deg);}
+}
+@-ms-keyframes spinnerRotateAnimation
+{
+  from{-ms-transform:rotate(0deg);}
+  to{-ms-transform:rotate(360deg);}
+}
+
+@keyframes spinnerRotateAnimation {
+  from {transform: rotate(0deg);}
+  to{transform: rotate(360deg);}
+}
+
+.spinAnimation{
+  -webkit-animation-name: spinnerRotateAnimation;
+  -webkit-animation-duration: 1s;
+  -webkit-animation-iteration-count: infinite;
+  -webkit-animation-timing-function: linear;
+  -moz-animation-name: spinnerRotateAnimation;
+  -moz-animation-duration: 1s;
+  -moz-animation-iteration-count: infinite;
+  -moz-animation-timing-function: linear;
+  -ms-animation-name: spinnerRotateAnimation;
+  -ms-animation-duration: 1s;
+  -ms-animation-iteration-count: infinite;
+  -ms-animation-timing-function: linear;
+}
+

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/app/jobmanager/jobs/job.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobs/job.html 
b/zeppelin-web/src/app/jobmanager/jobs/job.html
new file mode 100644
index 0000000..0a6ad67
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobs/job.html
@@ -0,0 +1,107 @@
+<!--
+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.
+-->
+
+<div class="job" data-ng-init="init(notebookJob)">
+  <div>
+    <div ng-include src="'app/jobmanager/jobs/job-control.html'"></div>
+    <span
+      class="job-types"
+      ng-switch="notebookJob.notebookType">
+      <i ng-switch-when="normal" class="icon-doc"></i>
+      <i ng-switch-when="cron" class="icon-clock"></i>
+      <i ng-switch-default class="icon-question"></i>
+    </span>
+    &nbsp;
+    <a style="text-decoration: none !important;" 
ng-href="#/notebook/{{notebookJob.notebookId}}">
+      <span>
+       {{notebookJob.notebookName}}
+      </span>
+      <span>
+        &nbsp;-&nbsp;
+      </span>
+      <span>
+        <span ng-if="notebookJob.interpreter === undefined" style="color: 
orange">
+          Interpreter is not set
+        </span>
+        <span ng-if="notebookJob.interpreter !== undefined" style="color: 
gray">
+          {{notebookJob.interpreter}}
+        </span>
+      </span>
+    </a>
+    <div ng-include src="'app/jobmanager/jobs/job-progressBar.html'"></div>
+  </div>
+
+  <div>
+    <span
+      ng-repeat="paragraphJob in notebookJob.paragraphs"
+      ng-switch="paragraphJob.status">
+      <a ng-switch-when="READY"
+         style="text-decoration: none !important;"
+         
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
+        <i style="color: green" class="fa fa-circle-o"
+           tooltip-placement="top-left"
+           tooltip="{{paragraphJob.name}} is READY">
+        </i>
+      </a>
+      <a ng-switch-when="FINISHED"
+         style="text-decoration: none !important;"
+         
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
+        <i style="color: green" class="fa fa-circle"
+           tooltip-placement="top-left"
+           tooltip="{{paragraphJob.name}} is FINISHED">
+        </i>
+      </a>
+      <a ng-switch-when="ABORT"
+         style="text-decoration: none !important;"
+         
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
+        <i style="color: orange" class="fa fa-circle"
+           tooltip-placement="top-left"
+           tooltip="{{paragraphJob.name}} is ABORT">
+        </i>
+      </a>
+      <a ng-switch-when="ERROR"
+         style="text-decoration: none !important;"
+         
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
+        <i style="color: red" class="fa fa-circle"
+           tooltip-placement="top-left"
+           tooltip="{{paragraphJob.name}} is ERROR">
+        </i>
+      </a>
+      <a ng-switch-when="PENDING"
+         style="text-decoration: none !important;"
+         
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
+        <i style="color: gray" class="fa fa-circle"
+           tooltip-placement="top-left"
+           tooltip="{{paragraphJob.name}} is PENDING">
+        </i>
+      </a>
+      <a ng-switch-when="RUNNING"
+         style="text-decoration: none !important;"
+         
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
+        <i style="color: blue" class="fa fa-spinner spinAnimation"
+           tooltip-placement="top-left"
+           tooltip="{{paragraphJob.name}} is RUNNING">
+        </i>
+      </a>
+      <a ng-switch-default class="icon-question"
+         style="text-decoration: none !important;"
+         
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
+        <i class="icon-question"
+           tooltip-placement="top-left"
+           tooltip="{{paragraphJob.name}} is {{paragraphJob.status}}">
+        </i>
+      </a>
+    </span>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/components/navbar/navbar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar.html 
b/zeppelin-web/src/components/navbar/navbar.html
index cc2df26..ab84764 100644
--- a/zeppelin-web/src/components/navbar/navbar.html
+++ b/zeppelin-web/src/components/navbar/navbar.html
@@ -38,6 +38,7 @@ limitations under the License.
             </div>
           </ul>
         </li>
+        <li><a href="#/jobmanager">Job</a></li>
       </ul>
 
       <ul class="nav navbar-nav navbar-right" style="margin-right:5px;">

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
----------------------------------------------------------------------
diff --git 
a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js 
b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
index 19afdc0..47bc854 100644
--- a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
+++ b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
@@ -59,6 +59,10 @@ angular.module('zeppelinWebApp').factory('websocketEvents', 
function($rootScope,
       $location.path('notebook/' + data.note.id);
     } else if (op === 'NOTES_INFO') {
       $rootScope.$broadcast('setNoteMenu', data.notes);
+    } else if (op === 'LIST_NOTEBOOK_JOBS') {
+      $rootScope.$broadcast('setNotebookJobs', data.notebookJobs);
+    } else if (op === 'LIST_UPDATE_NOTEBOOK_JOBS') {
+      $rootScope.$broadcast('setUpdateNotebookJobs', data.notebookRunningJobs);
     } else if (op === 'AUTH_INFO') {
       BootstrapDialog.show({
           closable: false,

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
----------------------------------------------------------------------
diff --git 
a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js 
b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
index 3b4df03..b636f0f 100644
--- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
+++ b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
@@ -163,6 +163,20 @@ 
angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope,
 
     isConnected: function(){
       return websocketEvents.isConnected();
+    },
+
+    getNotebookJobsList: function() {
+      websocketEvents.sendNewEvent({op: 'LIST_NOTEBOOK_JOBS'});
+    },
+
+    getUpdateNotebookJobsList: function(lastUpdateServerUnixTime) {
+      websocketEvents.sendNewEvent(
+        {op: 'LIST_UPDATE_NOTEBOOK_JOBS', data : {lastUpdateUnixTime : 
lastUpdateServerUnixTime*1}}
+      );
+    },
+
+    unsubscribeJobManager: function() {
+      websocketEvents.sendNewEvent({op: 'UNSUBSCRIBE_JOBMANAGER'});
     }
 
   };

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-web/src/index.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index 142d3ef..444195c 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -54,6 +54,8 @@ limitations under the License.
     <link rel="stylesheet" href="app/search/search.css" />
     <link rel="stylesheet" href="app/notebook/notebook.css" />
     <link rel="stylesheet" href="app/notebook/paragraph/paragraph.css" />
+    <link rel="stylesheet" href="app/jobmanager/jobmanager.css">
+    <link rel="stylesheet" href="app/jobmanager/jobs/job.css">
     <link rel="stylesheet" href="app/interpreter/interpreter.css" />
     <link rel="stylesheet" href="app/credential/credential.css" />
     <link rel="stylesheet" href="app/configuration/configuration.css" />
@@ -147,6 +149,8 @@ limitations under the License.
     <script src="app/app.controller.js"></script>
     <script src="app/home/home.controller.js"></script>
     <script src="app/notebook/notebook.controller.js"></script>
+    <script src="app/jobmanager/jobmanager.controller.js"></script>
+    <script src="app/jobmanager/jobs/job.controller.js"></script>
     <script src="app/interpreter/interpreter.controller.js"></script>
     <script src="app/interpreter/interpreter.filter.js"></script>
     <script src="app/credential/credential.controller.js"></script>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ca4e587f/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
index ab2ce5d..7073520 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
@@ -605,7 +605,7 @@ public class Notebook implements NoteEventListener {
 
       // set notebook Name
       String notebookName = note.getName();
-      if (notebookName != null) {
+      if (notebookName != null && !notebookName.equals("")) {
         info.put("notebookName", note.getName());
       } else {
         info.put("notebookName", "Note " + note.id());
@@ -635,9 +635,9 @@ public class Notebook implements NoteEventListener {
 
         // is update notebook for last server update time.
         if (lastRunningUnixTime > lastUpdateServerUnixTime) {
-          paragraphsInfo.add(paragraphItem);
           isUpdateNotebook = true;
         }
+        paragraphsInfo.add(paragraphItem);
       }
 
       // set interpreter bind type

Reply via email to