You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@zeppelin.apache.org by GitBox <gi...@apache.org> on 2018/12/26 10:16:32 UTC

[GitHub] Savalek closed pull request #3274: [ZP-94] Синхронизация с Apache

Savalek closed pull request #3274: [ZP-94] Синхронизация с Apache
URL: https://github.com/apache/zeppelin/pull/3274
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
index 4d258342b8..9e1e5dd465 100644
--- a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
+++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
@@ -729,7 +729,11 @@ private InterpreterResult executeSql(String propertyKey, String sql,
               getProperty(String.format(STATEMENT_PRECODE_KEY_TEMPLATE, propertyKey));
           
           if (StringUtils.isNotBlank(statementPrecode)) {
-            statement.execute(statementPrecode);
+            String noteId = interpreterContext.getNoteId();
+            String contextStatementPrecode = statementPrecode
+                .replace("#{noteId}", noteId != null ? noteId : "")
+                .replace("#{user}", user);
+            statement.execute(contextStatementPrecode);
           }
 
           boolean isResultSetAvailable = statement.execute(sqlToExecute);
diff --git a/zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java b/zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
index 030ddeb2bd..d1481ea265 100644
--- a/zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
+++ b/zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
@@ -109,6 +109,8 @@ protected void createNewNote() {
 
     WebDriverWait block = new WebDriverWait(driver, MAX_BROWSER_TIMEOUT_SEC);
     block.until(ExpectedConditions.visibilityOfElementLocated(By.id("noteCreateModal")));
+    WebElement element = pollingWait(By.xpath("//*[@id=\"noteName\"]"), MAX_BROWSER_TIMEOUT_SEC);
+    element.sendKeys(element.getAttribute("value").replaceFirst(".*/", ""));
     clickAndWait(By.id("createNoteButton"));
     block.until(ExpectedConditions.invisibilityOfElementLocated(By.id("createNoteButton")));
   }
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 40c7461caf..cb46382fa9 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
@@ -1611,10 +1611,12 @@ public void onStatusChange(Paragraph p, Status before, Status after) {
 
     if (p.isTerminated()) {
       if (p.getStatus() == Status.FINISHED) {
-        LOG.info("Job {} is finished successfully, status: {}", p.getId(), p.getStatus());
+        LOG.info("Note {}, job {} is finished successfully, status: {}",
+                p.getNote().getId(), p.getId(), p.getStatus());
       } else {
-        LOG.warn("Job {} is finished, status: {}, exception: {}, result: {}", p.getId(),
-            p.getStatus(), p.getException(), p.getReturn());
+        LOG.warn("Note {}. job {} is finished, status: {}, exception: {}, "
+                + "result\n@@@@@ Result start @@@@@\n{}\n@@@@@ Result end @@@@@",
+                p.getNote().getId(), p.getId(), p.getStatus(), p.getException(), p.getReturn());
       }
 
       try {
diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html
index 668697bdcb..0afc448626 100644
--- a/zeppelin-web/src/app/notebook/notebook-actionBar.html
+++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html
@@ -30,22 +30,25 @@ <h3>
       <span class="labelBtn btn-group">
       <button type="button"
               class="btn btn-default btn-xs"
-              ng-click="runAllParagraphs(note.id)"
-              ng-class="{'disabled':isNoteRunning()}"
-              tooltip-placement="bottom" uib-tooltip="Run all paragraphs"
+              ng-click="isSelectionMode() ? selectedParagraphsAction('run', true) : runAllParagraphs(note.id)"
+              ng-class="{'disabled':isNoteRunning(), 'select-mode-background': isSelectionMode()}"
+              tooltip-placement="bottom"
+              uib-tooltip="{{isSelectionMode() ? 'Run selected paragraphs' : 'Run all paragraphs'}}"
               ng-disabled="revisionView">
         <i class="icon-control-play"></i>
       </button>
       <button type="button"
               class="btn btn-default btn-xs"
-              ng-click="toggleAllEditor()"
+              ng-click="isSelectionMode() ? selectedParagraphsAction('toggleEditor') : toggleAllEditor()"
+              ng-class="{'select-mode-background': isSelectionMode()}"
               ng-hide="viewOnly"
               tooltip-placement="bottom" uib-tooltip="Show/hide the code"
               ng-disabled="revisionView">
-        <i ng-class="editorToggled ?  'fa icon-size-fullscreen' :'fa icon-size-actual'"></i></button>
+        <i ng-class="editorToggled ?  'fa icon-size-actual' : 'fa icon-size-fullscreen'"></i></button>
       <button type="button"
               class="btn btn-default btn-xs"
-              ng-click="toggleAllTable()"
+              ng-click="isSelectionMode() ? selectedParagraphsAction('toggleTable') : toggleAllTable()"
+              ng-class="{'select-mode-background': isSelectionMode()}"
               ng-hide="viewOnly"
               tooltip-placement="bottom" uib-tooltip="Show/hide the output"
               ng-disabled="revisionView">
@@ -53,13 +56,22 @@ <h3>
       </button>
       <button type="button"
               class="btn btn-default btn-xs"
-              ng-click="clearAllParagraphOutput(note.id)"
+              ng-click="isSelectionMode() ? selectedParagraphsAction('clearOutput', true) : clearAllParagraphOutput(note.id)"
               ng-hide="viewOnly"
-              ng-class="{'disabled':isNoteRunning()}"
+              ng-class="{'disabled':isNoteRunning(), 'select-mode-background': isSelectionMode()}"
               tooltip-placement="bottom" uib-tooltip="Clear output"
               ng-disabled="revisionView">
         <i class="fa fa-eraser"></i>
       </button>
+      <button type="button"
+              class="btn btn-default btn-xs"
+              ng-class="{'disabled':isNoteRunning(), 'select-mode-background': isSelectionMode()}"
+              ng-if="isSelectionMode()"
+              ng-click="selectedParagraphsAction('toggleEnableRun')"
+              tooltip-placement="bottom" uib-tooltip="Enable/Disable for running"
+              ng-disabled="revisionView">
+        <i ng-class="!isEnableRunToggled ? 'fa fa-lock' : 'fa fa-unlock'"></i>
+      </button>
 
       <button type="button"
               class="btn btn-default btn-xs"
@@ -98,6 +110,24 @@ <h3>
               ng-disabled="revisionView">
         <i class="fa fa-users"></i>
       </button>
+      <button type="button"
+              class="btn btn-default btn-xs"
+              ng-class="{'disabled': isNoteRunning(), 'select-mode-background': isSelectionMode()}"
+              ng-if="isSelectionMode()"
+              ng-click="selectedParagraphsAction('delete', true)"
+              tooltip-placement="bottom" uib-tooltip="Delete selected paragraphs"
+              ng-disabled="revisionView">
+        <i class="fa fa-recycle"></i>
+      </button>
+      <button type="button"
+              class="btn btn-default btn-xs"
+              ng-class="{'select-mode-background': isSelectionMode()}"
+              ng-hide="viewOnly"
+              ng-if="isSelectionMode()"
+              ng-click="clearSelection()"
+              tooltip-placement="bottom" uib-tooltip="Reset selections">
+        <i class="fa fa-times-circle-o"></i>
+    </button>
     </span>
 
     <span class="labelBtn btn-group" role="group" ng-if="isRevisionSupported()" >
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js
index 426667d6c9..753c75f374 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -32,6 +32,7 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
   $scope.disableForms = false;
   $scope.editorToggled = false;
   $scope.tableToggled = false;
+  $scope.isEnableRunToggled = true;
   $scope.viewOnly = false;
   $scope.showSetting = false;
   $scope.showRevisionsComparator = false;
@@ -39,6 +40,7 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
   $scope.collaborativeModeUsers = [];
   $scope.looknfeelOption = ['default', 'simple', 'report'];
   $scope.noteFormTitle = null;
+  $scope.selectedParagraphsIds = new Set();
   $scope.cronOption = [
     {name: 'None', value: undefined},
     {name: '1m', value: '0 0/1 * * * ?'},
@@ -599,6 +601,7 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
     if ($scope.paragraphUrl || $scope.revisionView === true) {
       return;
     }
+    $scope.selectedParagraphsIds.delete(paragraphId);
     removePara(paragraphId);
   });
 
@@ -627,6 +630,120 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
     websocketMsgSrv.getInterpreterBindings($scope.note.id);
   };
 
+  $scope.toggleSelection = function(paragraphId) {
+    let paragraphs = $scope.selectedParagraphsIds;
+    if (paragraphs.has(paragraphId)) {
+      paragraphs.delete(paragraphId);
+    } else {
+      paragraphs.add(paragraphId);
+    }
+  };
+
+  $scope.clearSelection = function() {
+    if ($scope.selectedParagraphsIds !== null) {
+      $scope.selectedParagraphsIds.clear();
+    }
+  };
+
+  $scope.isSelectionMode = function() {
+    if ($scope.selectedParagraphsIds === null || $scope.selectedParagraphsIds.size === 0) {
+      return false;
+    }
+    return true;
+  };
+
+  $scope.showConfirmDialog = function(dialogMessage, action) {
+    BootstrapDialog.confirm({
+      closable: true,
+      title: '',
+      message: dialogMessage,
+      callback: function(result) {
+        if (result) {
+          action();
+        }
+      },
+    });
+  };
+
+  $scope.selectedParagraphsAction = function(type, requestConfirm) {
+    let noteId = $scope.note.id;
+    let action;
+    let broadcastAction = function(data) {
+      return () => $scope.$broadcast('multipleAction', type, $scope.selectedParagraphsIds, data);
+    };
+
+    switch (type) {
+      case 'clearOutput':
+        action = broadcastAction();
+        break;
+
+      case 'toggleTable':
+        $scope.tableToggled = !$scope.tableToggled;
+        action = broadcastAction({toggleTableStatus: $scope.tableToggled});
+        break;
+
+      case 'toggleEditor':
+        $scope.editorToggled = !$scope.editorToggled;
+        action = broadcastAction({toggleEditorStatus: $scope.editorToggled});
+        break;
+
+      case 'toggleEnableRun':
+        $scope.isEnableRunToggled = !$scope.isEnableRunToggled;
+        action = broadcastAction({toggleEnableRunStatus: $scope.isEnableRunToggled});
+        break;
+
+      case 'delete':
+        if ($scope.note.paragraphs.length <= $scope.selectedParagraphsIds.size) {
+          BootstrapDialog.alert({
+            closable: true,
+            message: 'All the paragraphs can\'t be deleted.',
+          });
+          return;
+        } else {
+          action = broadcastAction();
+        }
+        break;
+
+      case 'run':
+        action = () => {
+          let paragraphs = $scope.note.paragraphs
+            .filter((p) => $scope.selectedParagraphsIds.has(p.id))
+            .map((p) => {
+              return {
+                id: p.id,
+                title: p.title,
+                paragraph: p.text,
+                config: p.config,
+                params: p.settings.params,
+              };
+            });
+          websocketMsgSrv.runAllParagraphs(noteId, paragraphs);
+        };
+        break;
+    }
+
+    if (requestConfirm) {
+      let messageToConfirm;
+      switch (type) {
+        case 'clearOutput':
+          messageToConfirm = 'Clear output ' + $scope.selectedParagraphsIds.size + ' selected paragraph(s)?';
+          break;
+        case 'run':
+          messageToConfirm = 'Run ' + $scope.selectedParagraphsIds.size + ' selected paragraph(s)?';
+          break;
+        case 'delete':
+          messageToConfirm = 'Delete ' + $scope.selectedParagraphsIds.size + ' selected paragraph(s)?';
+          break;
+        default:
+          messageToConfirm = 'Do \'' + type + '\'?';
+      }
+
+      $scope.showConfirmDialog(messageToConfirm, action);
+    } else {
+      action();
+    }
+  };
+
   $scope.$on('interpreterBindings', function(event, data) {
     $scope.interpreterBindings = data.interpreterBindings;
     $scope.interpreterBindingsOrig = angular.copy($scope.interpreterBindings); // to check dirty
diff --git a/zeppelin-web/src/app/notebook/notebook.css b/zeppelin-web/src/app/notebook/notebook.css
index 0b8e48ae3d..e93bfe85dd 100644
--- a/zeppelin-web/src/app/notebook/notebook.css
+++ b/zeppelin-web/src/app/notebook/notebook.css
@@ -450,3 +450,29 @@
 .tooltip-inner {
   max-width: none !important;
 }
+
+.select-mode-background {
+  background-color: #b9f1ff;
+}
+
+.select-mode-background.disabled{
+  background-color: rgba(185, 241, 255, 0.65);
+}
+
+.select-mode-background:hover,
+.select-mode-background:focus {
+  background-color: #9bd0ff;
+}
+
+.selected-paragraph {
+  background-image: radial-gradient(circle farthest-corner at right top,rgba(0, 196, 243, 0.53), rgba(75, 212, 246, 0.17) 100px);
+}
+
+.selected-paragraph .highcharts-background,
+.selected-paragraph .highcharts-button-box{
+  fill: none;
+}
+
+.selected-paragraph.focused {
+  border-color: #a3dbfb !important;
+}
diff --git a/zeppelin-web/src/app/notebook/notebook.html b/zeppelin-web/src/app/notebook/notebook.html
index ae23f76049..9545fe8b8c 100644
--- a/zeppelin-web/src/app/notebook/notebook.html
+++ b/zeppelin-web/src/app/notebook/notebook.html
@@ -172,7 +172,8 @@ <h4 class="plus-sign" ng-class="{'new-paragraph-disable': isNoteRunning}">&#43;
     <div id="{{currentParagraph.id}}_paragraphColumn"
          ng-include src="'app/notebook/paragraph/paragraph.html'"
          ng-class="{'paragraph-space box paragraph-margin': !asIframe, 'focused': paragraphFocused,
-                    'lastEmptyParagraph': !paragraph.text && !paragraph.result}"
+                    'lastEmptyParagraph': !paragraph.text && !paragraph.result,
+                    'selected-paragraph': selectedParagraphsIds.has(currentParagraph.id)}"
          ng-hide="currentParagraph.config.tableHide && viewOnly"
          ng-dblclick="paragraphOnDoubleClick(currentParagraph.id);">
     </div>
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index 5f01c7e802..ee92f75e68 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -1716,6 +1716,9 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
         $scope.changeColWidth($scope.paragraph, Math.max(1, $scope.paragraph.config.colWidth - 1));
       } else if (keyEvent.ctrlKey && keyEvent.shiftKey && keyCode === 187) { // Ctrl + Shift + =
         $scope.changeColWidth($scope.paragraph, Math.min(12, $scope.paragraph.config.colWidth + 1));
+      } else if (keyEvent.ctrlKey && keyEvent.shiftKey && keyCode === 83) { // Ctrl + Shift + S
+        $scope.toggleSelection(paragraphId);
+        $scope.$apply();
       } else if (keyEvent.ctrlKey && keyEvent.altKey && keyCode === 84) { // Ctrl + Alt + t
         if ($scope.paragraph.config.title) {
           $scope.hideTitle($scope.paragraph);
@@ -1949,4 +1952,38 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
       $scope.$emit('occurrencesExists', searchRanges.length);
     }
   });
+
+  $scope.$on('multipleAction', function(event, type, ids, data) {
+    if (!ids.has($scope.paragraph.id)) {
+      return;
+    }
+
+    switch (type) {
+      case 'clearOutput':
+        $scope.clearParagraphOutput($scope.paragraph);
+        break;
+
+      case 'toggleTable':
+        $scope.paragraph.config.tableHide = data.toggleTableStatus;
+        commitParagraph($scope.paragraph);
+        break;
+
+      case 'toggleEditor':
+        if (data.toggleEditorStatus) {
+          $scope.openEditor($scope.paragraph);
+        } else {
+          $scope.closeEditor($scope.paragraph);
+        }
+        break;
+
+      case 'toggleEnableRun':
+        $scope.paragraph.config.enabled = data.toggleEnableRunStatus;
+        commitParagraph($scope.paragraph);
+        break;
+
+      case 'delete':
+        websocketMsgSrv.removeParagraph($scope.paragraph.id);
+        break;
+    }
+  });
 }
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.css b/zeppelin-web/src/app/notebook/paragraph/paragraph.css
index 513556221c..ca35128e6a 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.css
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.css
@@ -176,7 +176,7 @@
 .paragraph .control {
   position: absolute;
   float: right;
-  background: rgba(255,255,255,0.85);
+  background: rgba(255, 255, 255, 0);
   color: #999;
   margin-top: 1px;
   margin-right: 5px;
@@ -199,6 +199,29 @@
   z-index: 10003;
 }
 
+.paragraph .check-button {
+  display: none;
+  position: absolute;
+  cursor: pointer;
+  right: -2px;
+  top: 7px;
+  font-size: 18px;
+  opacity: 0.4;
+  width: 22px;
+  height: 22px;
+}
+
+.paragraph .check-button-display-always,
+.paragraph:hover .check-button {
+  display: block;
+}
+
+@media (min-width: 992px) {
+  .paragraph .check-button {
+    top: 0 !important;
+  }
+}
+
 /*
   Paragraph Menu
 */
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.html b/zeppelin-web/src/app/notebook/paragraph/paragraph.html
index 10afd1754c..f780e1e862 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.html
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.html
@@ -75,6 +75,12 @@
 
   <div ng-include src="'app/notebook/paragraph/paragraph-control.html'"></div>
 
+  <span class="check-button"
+        tooltip-placement="bottom-right"
+        ng-class="{'check-button-display-always': selectedParagraphsIds.size > 0}"
+        uib-tooltip="{{selectedParagraphsIds.has(paragraph.id) ?  'Remove this paragraph from selection (Ctrl + Shift + S)' : 'Add this paragraph to selection (Ctrl + Shift + S)'}}"
+        ng-click="toggleSelection(paragraph.id)"><i class="fa {{selectedParagraphsIds.has(paragraph.id) ? 'fa-check-square-o' : 'fa-square-o'}}" aria-hidden="true"></i></span>
+
   <div ng-if="!asIframe" class="paragraphFooter">
     <div ng-show="!paragraph.config.tableHide && !viewOnly"
          id="{{paragraph.id}}_executionTime"
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html b/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
index 6b34977f8f..54b906a04e 100644
--- a/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
@@ -73,26 +73,6 @@
   </ul>
 </div>
 
-<div class="btn-group"
-     ng-if="(type == 'TABLE' || type == 'NETWORK') && !asIframe && !viewOnly"
-     style="margin-bottom: 10px;">
-  <button type="button" class="btn btn-default btn-sm"
-          style="margin-left:10px"
-          ng-click="exportToDSV(',')"
-          uib-tooltip="Download Data as CSV" tooltip-placement="bottom">
-    <i class="fa fa-download"></i>
-  </button>
-  <button type="button" class="btn btn-default btn-sm dropdown-toggle caretBtn"
-          data-toggle="dropdown">
-    <span class="caret" style="margin: 0px;"></span>
-    <span class="sr-only">Toggle Dropdown</span>
-  </button>
-  <ul class="dropdown-menu" role="menu" style="min-width: 70px;">
-    <li ng-click="exportToDSV(',')"><a>CSV</a></li>
-    <li ng-click="exportToDSV('\t')"><a>TSV</a></li>
-  </ul>
-</div>
-
 <span
    ng-if="(type == 'TABLE' || type == 'NETWORK') && !config.helium.activeApp && !asIframe && !viewOnly"
    style="margin-left:10px; cursor:pointer; display: inline-block; vertical-align:top; position: relative; line-height:30px;">
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
index 86f112bda3..d54f541fd0 100644
--- a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
@@ -12,8 +12,6 @@
  * limitations under the License.
  */
 
-import moment from 'moment';
-
 import DatasetFactory from '../../../tabledata/datasetfactory';
 import TableVisualization from '../../../visualization/builtins/visualization-table';
 import BarchartVisualization from '../../../visualization/builtins/visualization-barchart';
@@ -33,7 +31,7 @@ angular.module('zeppelinWebApp').controller('ResultCtrl', ResultCtrl);
 
 function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location,
                     $timeout, $compile, $http, $q, $templateCache, $templateRequest, $sce, websocketMsgSrv,
-                    baseUrlSrv, ngToast, saveAsService, noteVarShareService, heliumService,
+                    baseUrlSrv, ngToast, noteVarShareService, heliumService,
                     uiGridConstants) {
   'ngInject';
 
@@ -858,43 +856,6 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
     commitParagraphResult(paragraph.title, paragraph.text, newConfig, newParams);
   };
 
-  $scope.exportToDSV = function(delimiter) {
-    let dsv = '';
-    let dateFinished = moment(paragraph.dateFinished).format('YYYY-MM-DD hh:mm:ss A');
-    let exportedFileName = paragraph.title ? paragraph.title + '_' + dateFinished : 'data_' + dateFinished;
-
-    for (let titleIndex in tableData.columns) {
-      if (tableData.columns.hasOwnProperty(titleIndex)) {
-        dsv += tableData.columns[titleIndex].name + delimiter;
-      }
-    }
-    dsv = dsv.substring(0, dsv.length - 1) + '\n';
-    for (let r in tableData.rows) {
-      if (tableData.rows.hasOwnProperty(r)) {
-        let row = tableData.rows[r];
-        let dsvRow = '';
-        for (let index in row) {
-          if (row.hasOwnProperty(index)) {
-            let stringValue = (row[index]).toString();
-            if (stringValue.indexOf(delimiter) > -1) {
-              dsvRow += '"' + stringValue + '"' + delimiter;
-            } else {
-              dsvRow += row[index] + delimiter;
-            }
-          }
-        }
-        dsv += dsvRow.substring(0, dsvRow.length - 1) + '\n';
-      }
-    }
-    let extension = '';
-    if (delimiter === '\t') {
-      extension = 'tsv';
-    } else if (delimiter === ',') {
-      extension = 'csv';
-    }
-    saveAsService.saveAs(dsv, exportedFileName, extension);
-  };
-
   $scope.getBase64ImageSrc = function(base64Data) {
     return 'data:image/png;base64,' + base64Data;
   };
diff --git a/zeppelin-web/src/app/notebook/shortcut.html b/zeppelin-web/src/app/notebook/shortcut.html
index 9bc55973a5..9e80cf14f9 100644
--- a/zeppelin-web/src/app/notebook/shortcut.html
+++ b/zeppelin-web/src/app/notebook/shortcut.html
@@ -246,6 +246,17 @@ <h4 class="shortcut-modal-title" id="myModalLabel">Keyboard shortcuts</h4>
             </td>
           </tr>
 
+          <tr>
+            <td>
+              <div class="col-md-8">Select/Deselect current paragraph</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">S</kbd>
+              </div>
+            </td>
+          </tr>
+
           <tr class="sub-title">
             <th style="width:70%">Editor Keyboard Shortcuts</th>
             <th></th>
diff --git a/zeppelin-web/src/app/visualization/builtins/visualization-table.js b/zeppelin-web/src/app/visualization/builtins/visualization-table.js
index 179fd9a93b..c3c3f6c72a 100644
--- a/zeppelin-web/src/app/visualization/builtins/visualization-table.js
+++ b/zeppelin-web/src/app/visualization/builtins/visualization-table.js
@@ -256,6 +256,15 @@ export default class TableVisualization extends Visualization {
             return this.context.col.colDef.type === TableColumnType.DATE;
           },
         },
+        {
+          title: 'Copy Column Name',
+          action: function() {
+            self.copyStringToClipboard(this.context.col.displayName);
+          },
+          active: function() {
+            self.copyStringToClipboard(this.context.col.displayName);
+          },
+        },
       ];
     });
   }
@@ -515,4 +524,14 @@ export default class TableVisualization extends Visualization {
       },
     };
   }
+
+  copyStringToClipboard(copyString) {
+    const strToClipboard = document.createElement('textarea');
+    strToClipboard.value = copyString;
+    document.body.appendChild(strToClipboard);
+    strToClipboard.select();
+    document.execCommand('copy');
+    document.body.removeChild(strToClipboard);
+    return;
+  }
 }
diff --git a/zeppelin-web/src/components/note-create/note-create.controller.js b/zeppelin-web/src/components/note-create/note-create.controller.js
index c273b85826..2467517a50 100644
--- a/zeppelin-web/src/components/note-create/note-create.controller.js
+++ b/zeppelin-web/src/components/note-create/note-create.controller.js
@@ -64,7 +64,7 @@ function NoteCreateCtrl($scope, noteListFactory, $routeParams, websocketMsgSrv)
         }
       }
     });
-    return (path ? path + '/' : '') + 'Untitled Note ' + newCount;
+    return (path ? path + '/' : 'Users/' + $scope.ticket.screenUsername + '/') + 'Untitled Note ' + newCount;
   };
 
   vm.cloneNoteName = function() {
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
index bd3104ff48..20be125d73 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
@@ -450,6 +450,9 @@ protected InterpreterResult jobRun() throws Throwable {
         p.settings.setParams(settings.getParams());
       }
 
+      LOGGER.info("End of Run paragraph [paragraph_id: {}, interpreter: {}, note_id: {}, user: {}]",
+          getId(), intpText, note.getId(), subject.getUser());
+
       return res;
     } finally {
       InterpreterContext.remove();


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services