You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by zj...@apache.org on 2018/12/05 01:43:09 UTC
zeppelin git commit: [ZEPPELIN-3095] fix UI when paragraphs run
sequential
Repository: zeppelin
Updated Branches:
refs/heads/master eb941056b -> dda5a1452
[ZEPPELIN-3095] fix UI when paragraphs run sequential
### What is this PR for?
This PR fixes the incorrect behavior of UI during the execution of the RunAllParagraphs.
Including: allows you to copy the note at runtime (before that, the new note was created only after the end of runAllParagraph), blocks some interface elements.
The following items are blocked:
**Paragraphs Control :**
* Move up
* Move down
* Insert new
* Clone paragraph
* Run all above
* Run all below
* Disable run
* Remove
**Note :**
* Insert new paragraph
### What type of PR is it?
Improvement
### What is the Jira issue?
[Zeppelin-3095](https://issues.apache.org/jira/browse/ZEPPELIN-3095)
[ZP-25]
### GIF
![peek 2018-11-12 16-20](https://user-images.githubusercontent.com/30798933/48350794-511c6400-e699-11e8-952a-76a4dcc9e7b0.gif)
### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? no
Author: Savalek <de...@mail.ru>
Closes #3226 from Savalek/ZP-25 and squashes the following commits:
cf9229bce [Savalek] [ZEPPELIN-3095] fix UI when paragraphs run sequential
Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/dda5a145
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/dda5a145
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/dda5a145
Branch: refs/heads/master
Commit: dda5a145249538eb5a49e452e34f9c5779e0ad87
Parents: eb94105
Author: Savalek <de...@mail.ru>
Authored: Fri Nov 30 11:05:42 2018 +0300
Committer: Jeff Zhang <zj...@apache.org>
Committed: Wed Dec 5 09:42:53 2018 +0800
----------------------------------------------------------------------
.../integration/ParagraphActionsIT.java | 2 +
.../zeppelin/service/NotebookService.java | 33 +++++++-----
.../apache/zeppelin/socket/NotebookServer.java | 16 ++++++
.../zeppelin/rest/AbstractTestRestApi.java | 1 +
.../zeppelin/rest/ZeppelinRestApiTest.java | 6 +--
.../src/app/notebook/notebook-actionBar.html | 2 +
zeppelin-web/src/app/notebook/notebook.css | 5 ++
zeppelin-web/src/app/notebook/notebook.html | 4 +-
.../notebook/paragraph/paragraph-control.html | 27 ++++++----
.../notebook/paragraph/paragraph.controller.js | 47 +++++++++++++++--
.../src/app/notebook/paragraph/paragraph.css | 5 ++
.../websocket/websocket-event.factory.js | 2 +
.../java/org/apache/zeppelin/notebook/Note.java | 55 +++++++++++---------
.../org/apache/zeppelin/notebook/Notebook.java | 18 ++++++-
.../zeppelin/notebook/ParagraphJobListener.java | 3 ++
.../zeppelin/notebook/socket/Message.java | 32 ++++++++++--
.../apache/zeppelin/notebook/NotebookTest.java | 13 +++--
17 files changed, 205 insertions(+), 66 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java b/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
index 416d44e..93cafc9 100644
--- a/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
+++ b/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
@@ -229,6 +229,8 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
getParagraphStatus(1), CoreMatchers.equalTo("READY")
);
+ driver.navigate().refresh();
+ ZeppelinITUtils.sleep(3000, false);
deleteTestNotebook(driver);
} catch (Exception e) {
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java b/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java
index 641e799..14b8e23 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java
@@ -387,21 +387,26 @@ public class NotebookService {
return;
}
- for (Map<String, Object> raw : paragraphs) {
- String paragraphId = (String) raw.get("id");
- if (paragraphId == null) {
- continue;
- }
- String text = (String) raw.get("paragraph");
- String title = (String) raw.get("title");
- Map<String, Object> params = (Map<String, Object>) raw.get("params");
- Map<String, Object> config = (Map<String, Object>) raw.get("config");
-
- if (!runParagraph(noteId, paragraphId, title, text, params, config, false, true,
- context, callback)) {
- // stop execution when one paragraph fails.
- break;
+ note.setRunning(true);
+ try {
+ for (Map<String, Object> raw : paragraphs) {
+ String paragraphId = (String) raw.get("id");
+ if (paragraphId == null) {
+ continue;
+ }
+ String text = (String) raw.get("paragraph");
+ String title = (String) raw.get("title");
+ Map<String, Object> params = (Map<String, Object>) raw.get("params");
+ Map<String, Object> config = (Map<String, Object>) raw.get("config");
+
+ if (!runParagraph(noteId, paragraphId, title, text, params, config, false, true,
+ context, callback)) {
+ // stop execution when one paragraph fails.
+ break;
+ }
}
+ } finally {
+ note.setRunning(false);
}
}
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
index 1c40d17..ac167d1 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
@@ -240,6 +240,14 @@ public class NotebookServer extends WebSocketServlet
throw new Exception("Anonymous access not allowed ");
}
+ if (Message.isDisabledForRunningNotes(messagereceived.op)) {
+ Note note = notebook.getNote((String) messagereceived.get("noteId"));
+ if (note != null && note.isRunning()) {
+ throw new Exception("Note is now running sequentially. Can not be performed: " +
+ messagereceived.op);
+ }
+ }
+
if (StringUtils.isEmpty(conn.getUser())) {
connectionManager.addUserConnection(messagereceived.principal, conn);
}
@@ -1644,6 +1652,14 @@ public class NotebookServer extends WebSocketServlet
// TODO
}
+ @Override
+ public void noteRunningStatusChange(String noteId, boolean newStatus) {
+ connectionManager.broadcast(
+ noteId,
+ new Message(OP.NOTE_RUNNING_STATUS
+ ).put("status", newStatus));
+ }
+
private void sendAllAngularObjects(Note note, String user, NotebookSocket conn)
throws IOException {
List<InterpreterSetting> settings =
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java
index b003dd3..e4d161f 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java
@@ -246,6 +246,7 @@ public abstract class AbstractTestRestApi {
if (started == false) {
throw new RuntimeException("Can not start Zeppelin server");
}
+ ZeppelinServer.notebook.setParagraphJobListener(ZeppelinServer.notebookWsServer);
LOG.info("Test Zeppelin stared.");
}
}
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
index 6993418..2e5a024 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
@@ -393,7 +393,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
ZeppelinServer.notebook.saveNote(note, anonymous);
String noteId = note.getId();
- note.runAll();
+ note.runAll(anonymous, true);
// wait until job is finished or timeout.
int timeout = 1;
while (!paragraph.isTerminated()) {
@@ -449,7 +449,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
ZeppelinServer.notebook.saveNote(note, anonymous);
String noteId = note.getId();
- note.runAll();
+ note.runAll(anonymous, true);
// assume that status of the paragraph is running
GetMethod get = httpGet("/notebook/job/" + noteId);
assertThat("test get note job: ", get, isAllowed());
@@ -496,7 +496,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
ZeppelinServer.notebook.saveNote(note, anonymous);
String noteId = note.getId();
- note.runAll();
+ note.runAll(anonymous, true);
// Call Run paragraph REST API
PostMethod postParagraph = httpPost("/notebook/job/" + noteId + "/" + paragraph.getId(),
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-web/src/app/notebook/notebook-actionBar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html
index cb49786..668697b 100644
--- a/zeppelin-web/src/app/notebook/notebook-actionBar.html
+++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html
@@ -232,6 +232,7 @@ limitations under the License.
class="btn btn-default btn-xs"
ng-click="removeNote(note.id)"
ng-hide="viewOnly"
+ ng-class="{'disabled':isNoteRunning()}"
tooltip-placement="bottom" uib-tooltip="Remove this note permanently"
ng-disabled="revisionView">
<i class="icon-trash"></i>
@@ -242,6 +243,7 @@ limitations under the License.
class="btn btn-default btn-xs"
ng-click="moveNoteToTrash(note.id)"
ng-hide="viewOnly"
+ ng-class="{'disabled':isNoteRunning()}"
tooltip-placement="bottom" uib-tooltip="Move this note to trash"
ng-disabled="revisionView">
<i class="icon-trash"></i>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-web/src/app/notebook/notebook.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.css b/zeppelin-web/src/app/notebook/notebook.css
index 2f6ff9a..0b8e48a 100644
--- a/zeppelin-web/src/app/notebook/notebook.css
+++ b/zeppelin-web/src/app/notebook/notebook.css
@@ -106,6 +106,11 @@
display: block;
}
+.new-paragraph-disable{
+ color: lightgray !important;
+ cursor: default !important;
+}
+
.plus-sign{
display: none;
height: 7px;
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-web/src/app/notebook/notebook.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.html b/zeppelin-web/src/app/notebook/notebook.html
index 179192b..ae23f76 100644
--- a/zeppelin-web/src/app/notebook/notebook.html
+++ b/zeppelin-web/src/app/notebook/notebook.html
@@ -167,7 +167,7 @@ limitations under the License.
ng-click="insertNew('above')"
ng-hide="viewOnly || asIframe || revisionView"
ng-class="{'first-paragraph': $first}">
- <h4 class="plus-sign">+ Add Paragraph</h4>
+ <h4 class="plus-sign" ng-class="{'new-paragraph-disable': isNoteRunning}">+ Add Paragraph</h4>
</div>
<div id="{{currentParagraph.id}}_paragraphColumn"
ng-include src="'app/notebook/paragraph/paragraph.html'"
@@ -177,7 +177,7 @@ limitations under the License.
ng-dblclick="paragraphOnDoubleClick(currentParagraph.id);">
</div>
<div class="new-paragraph last-paragraph" ng-click="insertNew('below');" ng-hide="!$last || viewOnly || asIframe || revisionView">
- <h4 class="plus-sign">+ Add Paragraph</h4>
+ <h4 class="plus-sign" ng-class="{'new-paragraph-disable': isNoteRunning}">+ Add Paragraph</h4>
</div>
</div>
<div style="clear:both;height:10px"></div>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
index 0b4ca1e..4ef430f 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
@@ -47,10 +47,12 @@ limitations under the License.
<span class="icon-control-play" style="cursor:pointer;color:#3071A9"
tooltip-placement="top" uib-tooltip="Run this paragraph (Shift+Enter)"
ng-click="runParagraphFromButton(getEditorValue())"
+ ng-class="{'item-disable' : isNoteRunning}"
ng-show="paragraph.status!='RUNNING' && paragraph.status!='PENDING' && paragraph.config.enabled"></span>
<span class="icon-control-pause" style="cursor:pointer;color:#CD5C5C"
tooltip-placement="top" uib-tooltip="Cancel (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+C)"
ng-click="cancelParagraph(paragraph)"
+ ng-class="{'item-disable' : isNoteRunning}"
ng-show="paragraph.status=='RUNNING' || paragraph.status=='PENDING'"></span>
<span ng-show="paragraph.runtimeInfos.jobUrl.length == 1">
<a href="{{paragraph.runtimeInfos.jobUrl[0]}}" target="_blank"><span class="fa fa-tasks"></span> Spark job </a>
@@ -123,25 +125,28 @@ limitations under the License.
</a>
</li>
<li>
- <a ng-click="moveUp(paragraph)" ng-hide="$first"><span class="icon-arrow-up shortcut-icon"></span>
+ <a ng-click="moveUp(paragraph)" ng-hide="$first" ng-class="{'item-disable' : isNoteRunning}">
+ <span class="icon-arrow-up shortcut-icon"></span>
<span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+K</span>
Move up
</a>
</li>
<li>
- <a ng-click="moveDown(paragraph)" ng-hide="$last"><span class="icon-arrow-down shortcut-icon"></span>
+ <a ng-click="moveDown(paragraph)" ng-hide="$last" ng-class="{'item-disable' : isNoteRunning}">
+ <span class="icon-arrow-down shortcut-icon"></span>
<span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+J</span>
Move down
</a>
</li>
<li>
- <a ng-click="insertNew('below')"><span class="icon-plus shortcut-icon"></span>
+ <a ng-click="insertNew('below')" ng-class="{'item-disable' : isNoteRunning}">
+ <span class="icon-plus shortcut-icon"></span>
<span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+B</span>
Insert new
</a>
</li>
<li>
- <a ng-click="runAllToThis(paragraph)" ng-hide="$first">
+ <a ng-click="runAllToThis(paragraph)" ng-hide="$first" ng-class="{'item-disable' : isNoteRunning}">
<span class="icon-action-redo shortcut-icon"
style="position: relative; transform: rotate(-90deg); left: -4px;">
</span>
@@ -150,7 +155,7 @@ limitations under the License.
</a>
</li>
<li>
- <a ng-click="runAllFromThis(paragraph)" ng-hide="$last">
+ <a ng-click="runAllFromThis(paragraph)" ng-hide="$last" ng-class="{'item-disable' : isNoteRunning}">
<span class="icon-action-undo shortcut-icon"
style="position: relative; transform: rotate(-90deg); left: -4px;">
</span>
@@ -159,7 +164,8 @@ limitations under the License.
</a>
</li>
<li>
- <a ng-click="copyParagraph(getEditorValue())"><span class="fa fa-copy shortcut-icon"></span>
+ <a ng-click="copyParagraph(getEditorValue())" ng-class="{'item-disable' : isNoteRunning}">
+ <span class="fa fa-copy shortcut-icon"></span>
<span class="shortcut-keys">Ctrl+Shift+C</span>
Clone paragraph
</a>
@@ -190,7 +196,8 @@ limitations under the License.
</a>
</li>
<li>
- <a ng-click="toggleEnableDisable(paragraph)"><span class="icon-control-play shortcut-icon"></span>
+ <a ng-click="toggleEnableDisable(paragraph)" ng-class="{'item-disable' : isNoteRunning}">
+ <span class="icon-control-play shortcut-icon"></span>
<span class="shortcut-keys">Ctrl+ {{ isMac ? 'Option' : 'Alt'}}+R</span>
{{paragraph.config.enabled ? "Disable" : "Enable"}} run
</a>
@@ -202,14 +209,16 @@ limitations under the License.
</a>
</li>
<li>
- <a ng-click="clearParagraphOutput(paragraph)"><span class="fa fa-eraser shortcut-icon"></span>
+ <a ng-click="clearParagraphOutput(paragraph)" ng-class="{'item-disable' : isNoteRunning}">
+ <span class="fa fa-eraser shortcut-icon"></span>
<span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+L</span>
Clear output
</a>
</li>
<li>
<!-- remove paragraph -->
- <a ng-click="removeParagraph(paragraph)" ng-if="note.paragraphs.length > 1"><span class="fa fa-times shortcut-icon"></span>
+ <a ng-click="removeParagraph(paragraph)" ng-if="note.paragraphs.length > 1" ng-class="{'item-disable' : isNoteRunning}">
+ <span class="fa fa-times shortcut-icon"></span>
<span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+D</span>
Remove
</a>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index b332b59..5f01c7e 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -45,6 +45,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
$scope.editor = null;
$scope.cursorPosition = null;
$scope.diffMatchPatch = new DiffMatchPatch();
+ $scope.isNoteRunning = false;
// transactional info for spell execution
$scope.spellTransaction = {
@@ -147,11 +148,20 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
$scope.paragraph.config = {};
}
+ $scope.isNoteRunning = !!(note && note.hasOwnProperty('info') &&
+ note.info.hasOwnProperty('isRunning')
+ && note.info.isRunning === true);
+
noteVarShareService.put($scope.paragraph.id + '_paragraphScope', paragraphScope);
initializeDefault($scope.paragraph.config);
};
+ $scope.$on('noteRunningStatus', function(event, status) {
+ $scope.isNoteRunning = status;
+ $scope.editor.setReadOnly(status);
+ });
+
const initializeDefault = function(config) {
let forms = $scope.paragraph.settings.forms;
@@ -250,7 +260,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
console.log('editor isnt loaded yet, returning');
return;
}
- if ($scope.revisionView === true) {
+ if ($scope.revisionView === true || $scope.isNoteRunning === true) {
$scope.editor.setReadOnly(true);
} else {
$scope.editor.setReadOnly(false);
@@ -266,6 +276,9 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
};
$scope.cancelParagraph = function(paragraph) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
console.log('Cancel %o', paragraph.id);
websocketMsgSrv.cancelParagraphRun(paragraph.id);
};
@@ -430,6 +443,9 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
};
$scope.toggleEnableDisable = function(paragraph) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
paragraph.config.enabled = !paragraph.config.enabled;
commitParagraph(paragraph);
};
@@ -472,15 +488,24 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
};
$scope.runParagraphFromButton = function() {
+ if ($scope.isNoteRunning) {
+ return;
+ }
// we come here from the view, so we don't need to call `$digest()`
$scope.runParagraph($scope.getEditorValue(), false, false);
};
$scope.runAllToThis = function(paragraph) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
$scope.$emit('runAllAbove', paragraph, true);
};
$scope.runAllFromThis = function(paragraph) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
$scope.$emit('runAllBelowAndCurrent', paragraph, true);
};
@@ -519,18 +544,30 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
};
$scope.moveUp = function(paragraph) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
$scope.$emit('moveParagraphUp', paragraph);
};
$scope.moveDown = function(paragraph) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
$scope.$emit('moveParagraphDown', paragraph);
};
$scope.insertNew = function(position) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
$scope.$emit('insertParagraph', $scope.paragraph.id, position);
};
$scope.copyPara = function(position) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
let editorValue = $scope.getEditorValue();
if (editorValue) {
$scope.copyParagraph(editorValue, position);
@@ -563,6 +600,9 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
};
$scope.removeParagraph = function(paragraph) {
+ if ($scope.isNoteRunning) {
+ return;
+ }
if ($scope.note.paragraphs.length === 1) {
BootstrapDialog.alert({
closable: true,
@@ -738,7 +778,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
$scope.editor.setHighlightActiveLine(false);
$scope.editor.getSession().setUseWrapMode(true);
$scope.editor.setTheme('ace/theme/chrome');
- $scope.editor.setReadOnly($scope.isRunning($scope.paragraph));
+ $scope.editor.setReadOnly($scope.isRunning($scope.paragraph) || $scope.isNoteRunning);
$scope.editor.setHighlightActiveLine($scope.paragraphFocused);
if ($scope.paragraphFocused) {
@@ -1488,7 +1528,8 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
$scope.paragraph.settings = newPara.settings;
$scope.paragraph.runtimeInfos = newPara.runtimeInfos;
if ($scope.editor) {
- $scope.editor.setReadOnly($scope.isRunning(newPara));
+ let isReadOnly = $scope.isRunning(newPara) || $scope.isNoteRunning;
+ $scope.editor.setReadOnly(isReadOnly);
}
if (!$scope.asIframe) {
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-web/src/app/notebook/paragraph/paragraph.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.css b/zeppelin-web/src/app/notebook/paragraph/paragraph.css
index b17272b..5135562 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.css
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.css
@@ -212,6 +212,11 @@
float: left;
}
+.item-disable {
+ color: rgba(128, 128, 128, 0.61) !important;
+ cursor: default !important;
+}
+
.dropdown-menu .shortcut-keys {
float: right;
color: #999;
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-web/src/components/websocket/websocket-event.factory.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/websocket/websocket-event.factory.js b/zeppelin-web/src/components/websocket/websocket-event.factory.js
index 25a3336..ef255e7 100644
--- a/zeppelin-web/src/components/websocket/websocket-event.factory.js
+++ b/zeppelin-web/src/components/websocket/websocket-event.factory.js
@@ -65,6 +65,8 @@ function WebsocketEventFactory($rootScope, $websocket, $location, baseUrlSrv, ng
$location.path('/notebook/' + data.note.id);
} else if (op === 'NOTES_INFO') {
$rootScope.$broadcast('setNoteMenu', data.notes);
+ } else if (op === 'NOTE_RUNNING_STATUS') {
+ $rootScope.$broadcast('noteRunningStatus', data.status);
} else if (op === 'LIST_NOTE_JOBS') {
$rootScope.$emit('jobmanager:set-jobs', data.noteJobs);
} else if (op === 'LIST_UPDATE_NOTE_JOBS') {
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
index 74b08b2..a2ba100 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
@@ -604,32 +604,21 @@ public class Note implements JsonSerializable {
}
}
- /**
- * Run all paragraphs sequentially. Only used for CronJob
- */
- public synchronized void runAll() {
- String cronExecutingUser = (String) getConfig().get("cronExecutingUser");
- String cronExecutingRoles = (String) getConfig().get("cronExecutingRoles");
- if (null == cronExecutingUser) {
- cronExecutingUser = "anonymous";
- }
- AuthenticationInfo authenticationInfo = new AuthenticationInfo(
- cronExecutingUser,
- StringUtils.isEmpty(cronExecutingRoles) ? null : cronExecutingRoles,
- null);
- runAll(authenticationInfo, true);
- }
-
public void runAll(AuthenticationInfo authenticationInfo, boolean blocking) {
- for (Paragraph p : getParagraphs()) {
- if (!p.isEnabled()) {
- continue;
- }
- p.setAuthenticationInfo(authenticationInfo);
- if (!run(p.getId(), blocking)) {
- logger.warn("Skip running the remain notes because paragraph {} fails", p.getId());
- break;
+ setRunning(true);
+ try {
+ for (Paragraph p : getParagraphs()) {
+ if (!p.isEnabled()) {
+ continue;
+ }
+ p.setAuthenticationInfo(authenticationInfo);
+ if (!run(p.getId(), blocking)) {
+ logger.warn("Skip running the remain notes because paragraph {} fails", p.getId());
+ break;
+ }
}
+ } finally {
+ setRunning(false);
}
}
@@ -651,7 +640,7 @@ public class Note implements JsonSerializable {
/**
* Return true if there is a running or pending paragraph
*/
- boolean isRunningOrPending() {
+ boolean haveRunningOrPendingParagraphs() {
synchronized (paragraphs) {
for (Paragraph p : paragraphs) {
Status status = p.getStatus();
@@ -787,6 +776,21 @@ public class Note implements JsonSerializable {
this.info = info;
}
+ public void setRunning(boolean runStatus) {
+ Map<String, Object> infoMap = getInfo();
+ boolean oldStatus = (boolean) infoMap.getOrDefault("isRunning", false);
+ if (oldStatus != runStatus) {
+ infoMap.put("isRunning", runStatus);
+ if (paragraphJobListener != null) {
+ paragraphJobListener.noteRunningStatusChange(this.id, runStatus);
+ }
+ }
+ }
+
+ public boolean isRunning() {
+ return (boolean) getInfo().getOrDefault("isRunning", false);
+ }
+
@Override
public String toString() {
if (this.path != null) {
@@ -804,6 +808,7 @@ public class Note implements JsonSerializable {
public static Note fromJson(String json) {
Note note = gson.fromJson(json, Note.class);
convertOldInput(note);
+ note.info.remove("isRunning");
note.postProcessParagraphs();
return note;
}
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/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 9e7eb35..671a0cb 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
@@ -18,6 +18,7 @@
package org.apache.zeppelin.notebook;
import com.google.common.collect.Sets;
+import org.apache.commons.lang.StringUtils;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
import org.apache.zeppelin.display.AngularObject;
@@ -549,7 +550,7 @@ public class Notebook {
String noteId = context.getJobDetail().getJobDataMap().getString("noteId");
Note note = notebook.getNote(noteId);
- if (note.isRunningOrPending()) {
+ if (note.haveRunningOrPendingParagraphs()) {
LOGGER.warn("execution of the cron job is skipped because there is a running or pending " +
"paragraph (note id: {})", noteId);
return;
@@ -560,7 +561,7 @@ public class Notebook {
return;
}
- note.runAll();
+ runAll(note);
boolean releaseResource = false;
String cronExecutingUser = null;
@@ -587,6 +588,19 @@ public class Notebook {
}
}
}
+
+ void runAll(Note note) {
+ String cronExecutingUser = (String) note.getConfig().get("cronExecutingUser");
+ String cronExecutingRoles = (String) note.getConfig().get("cronExecutingRoles");
+ if (null == cronExecutingUser) {
+ cronExecutingUser = "anonymous";
+ }
+ AuthenticationInfo authenticationInfo = new AuthenticationInfo(
+ cronExecutingUser,
+ StringUtils.isEmpty(cronExecutingRoles) ? null : cronExecutingRoles,
+ null);
+ note.runAll(authenticationInfo, true);
+ }
}
public void refreshCron(String id) {
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java
index 8743fb7..1d49eff 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java
@@ -31,4 +31,7 @@ public interface ParagraphJobListener extends JobListener<Paragraph> {
void onOutputAppend(Paragraph paragraph, int idx, String output);
void onOutputUpdate(Paragraph paragraph, int idx, InterpreterResultMessage msg);
void onOutputUpdateAll(Paragraph paragraph, List<InterpreterResultMessage> msgs);
+
+ //TODO(savalek) Temporary solution. Need to refactor cron to be able to notify frontend directly.
+ void noteRunningStatusChange(String noteId, boolean newStatus);
}
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java
index 06499f3..f9894c8 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java
@@ -18,12 +18,14 @@
package org.apache.zeppelin.notebook.socket;
import com.google.gson.Gson;
-import org.apache.zeppelin.common.JsonSerializable;
-import org.slf4j.Logger;
-
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
-
+import java.util.Set;
+import org.apache.zeppelin.common.JsonSerializable;
+import org.slf4j.Logger;
/**
* Zeppelin websocket massage template class.
*/
@@ -188,9 +190,27 @@ public class Message implements JsonSerializable {
INTERPRETER_INSTALL_RESULT, // [s-c] Status of an interpreter installation
COLLABORATIVE_MODE_STATUS, // [s-c] collaborative mode status
PATCH_PARAGRAPH, // [c-s][s-c] patch editor text
+ NOTE_RUNNING_STATUS, // [s-c] sequential run status will be change
NOTICE // [s-c] Notice
}
+ // these messages will be ignored during the sequential run of the note
+ private static final Set<OP> disabledForRunningNoteMessages = Collections
+ .unmodifiableSet(new HashSet<>(Arrays.asList(
+ OP.COMMIT_PARAGRAPH,
+ OP.RUN_PARAGRAPH,
+ OP.RUN_PARAGRAPH_USING_SPELL,
+ OP.RUN_ALL_PARAGRAPHS,
+ OP.PARAGRAPH_CLEAR_OUTPUT,
+ OP.PARAGRAPH_CLEAR_ALL_OUTPUT,
+ OP.INSERT_PARAGRAPH,
+ OP.MOVE_PARAGRAPH,
+ OP.COPY_PARAGRAPH,
+ OP.PARAGRAPH_REMOVE,
+ OP.MOVE_NOTE_TO_TRASH,
+ OP.DEL_NOTE,
+ OP.PATCH_PARAGRAPH)));
+
private static final Gson gson = new Gson();
public static final Message EMPTY = new Message(null);
@@ -213,6 +233,10 @@ public class Message implements JsonSerializable {
return data.get(k);
}
+ public static boolean isDisabledForRunningNotes(OP eventType) {
+ return disabledForRunningNoteMessages.contains(eventType);
+ }
+
public <T> T getType(String key) {
return (T) data.get(key);
}
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/dda5a145/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
index 147b0c8..4e2d95a 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
@@ -451,7 +451,7 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo
p3.setText("%mock1 p3");
// when
- note.runAll();
+ note.runAll(anonymous, true);
assertEquals("repl1: p1", p1.getReturn().message().get(0).getData());
assertNull(p2.getReturn());
@@ -809,7 +809,7 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo
String simpleText = "hello world";
p.setText(simpleText);
- note.runAll();
+ note.runAll(anonymous, true);
String exportedNoteJson = notebook.exportNote(note.getId());
@@ -841,7 +841,7 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo
final Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
p.setText("hello world");
- note.runAll();
+ note.runAll(anonymous, true);
p.setStatus(Status.RUNNING);
Note cloneNote = notebook.cloneNote(note.getId(), "clone note", anonymous);
@@ -877,7 +877,7 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo
for (InterpreterGroup intpGroup : interpreterSettingManager.getAllInterpreterGroup()) {
intpGroup.setResourcePool(new LocalResourcePool(intpGroup.getId()));
}
- note.runAll();
+ note.runAll(anonymous, true);
assertEquals(2, interpreterSettingManager.getAllResources().size());
@@ -1491,6 +1491,11 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo
}
@Override
+ public void noteRunningStatusChange(String noteId, boolean newStatus) {
+
+ }
+
+ @Override
public void onProgressUpdate(Paragraph paragraph, int progress) {
}