Render multiple results

This commit is contained in:
Lee moon soo 2016-11-19 12:06:39 -08:00
parent 95b6037425
commit 57d6b2f647
20 changed files with 663 additions and 642 deletions

View file

@ -462,7 +462,7 @@ public class RemoteInterpreterServer
combinedResult);
}
*/
return resultMessages;
return new InterpreterResult(result.code(), resultMessages);
} finally {
InterpreterContext.remove();
}

View file

@ -51,7 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-17")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-18")
public class InterpreterCompletion implements org.apache.thrift.TBase<InterpreterCompletion, InterpreterCompletion._Fields>, java.io.Serializable, Cloneable, Comparable<InterpreterCompletion> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("InterpreterCompletion");

View file

@ -51,7 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-17")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-18")
public class RemoteApplicationResult implements org.apache.thrift.TBase<RemoteApplicationResult, RemoteApplicationResult._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteApplicationResult> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteApplicationResult");

View file

@ -51,7 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-17")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-18")
public class RemoteInterpreterContext implements org.apache.thrift.TBase<RemoteInterpreterContext, RemoteInterpreterContext._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterContext> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterContext");

View file

@ -51,7 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-17")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-18")
public class RemoteInterpreterEvent implements org.apache.thrift.TBase<RemoteInterpreterEvent, RemoteInterpreterEvent._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterEvent> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterEvent");

View file

@ -89,7 +89,7 @@ public enum RemoteInterpreterEventType implements org.apache.thrift.TEnum {
return ANGULAR_REGISTRY_PUSH;
case 13:
return APP_STATUS_UPDATE;
case 12:
case 14:
return META_INFOS;
default:
return null;

View file

@ -51,7 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-17")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-18")
public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteInterpreterResult, RemoteInterpreterResult._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterResult> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterResult");

View file

@ -51,7 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-14")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-18")
public class RemoteInterpreterResultMessage implements org.apache.thrift.TBase<RemoteInterpreterResultMessage, RemoteInterpreterResultMessage._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterResultMessage> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterResultMessage");

View file

@ -51,7 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-17")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-18")
public class RemoteInterpreterService {
public interface Iface {

View file

@ -99,83 +99,6 @@
var angularObjectRegistry = {};
/**
* Built-in visualizations
*/
$scope.builtInTableDataVisualizationList = [
{
id: 'table', // paragraph.config.graph.mode
name: 'Table', // human readable name. tooltip
icon: 'fa fa-table'
},
{
id: 'multiBarChart',
name: 'Bar Chart',
icon: 'fa fa-bar-chart',
transformation: 'pivot'
},
{
id: 'pieChart',
name: 'Pie Chart',
icon: 'fa fa-pie-chart',
transformation: 'pivot'
},
{
id: 'stackedAreaChart',
name: 'Area Chart',
icon: 'fa fa-area-chart',
transformation: 'pivot'
},
{
id: 'lineChart',
name: 'Line Chart',
icon: 'fa fa-line-chart',
transformation: 'pivot'
},
{
id: 'scatterChart',
name: 'Scatter Chart',
icon: 'cf cf-scatter-chart'
}
];
/**
* Holds class and actual runtime instance and related infos of built-in visualizations
*/
var builtInVisualizations = {
'table': {
class: zeppelin.TableVisualization,
instance: undefined // created from setGraphMode()
},
'multiBarChart': {
class: zeppelin.BarchartVisualization,
instance: undefined
},
'pieChart': {
class: zeppelin.PiechartVisualization,
instance: undefined
},
'stackedAreaChart': {
class: zeppelin.AreachartVisualization,
instance: undefined
},
'lineChart': {
class: zeppelin.LinechartVisualization,
instance: undefined
},
'scatterChart': {
class: zeppelin.ScatterchartVisualization,
instance: undefined
}
};
/**
* TableData instance
*/
var tableData;
// available columns in tabledata
$scope.tableDataColumns = [];
// Controller init
$scope.init = function(newParagraph, note) {
@ -195,21 +118,6 @@
initializeDefault();
if ($scope.getResultType() === 'TABLE') {
var TableData = zeppelin.TableData;
tableData = new TableData();
tableData.loadParagraphResult($scope.paragraph.result);
$scope.tableDataColumns = tableData.columns;
$scope.tableDataComment = tableData.comment;
$scope.setGraphMode($scope.getGraphMode(), false, false);
} else if ($scope.getResultType() === 'HTML') {
$scope.renderHtml();
} else if ($scope.getResultType() === 'ANGULAR') {
$scope.renderAngular();
} else if ($scope.getResultType() === 'TEXT') {
$scope.renderText();
}
getApplicationStates();
getSuggestions();
@ -220,89 +128,6 @@
}
};
$scope.renderHtml = function() {
var retryRenderer = function() {
var htmlEl = angular.element('#p' + $scope.paragraph.id + '_html');
if (htmlEl.length) {
try {
htmlEl.html($scope.paragraph.result.msg);
htmlEl.find('pre code').each(function(i, e) {
hljs.highlightBlock(e);
});
/*eslint new-cap: [2, {"capIsNewExceptions": ["MathJax.Hub.Queue"]}]*/
MathJax.Hub.Queue(['Typeset', MathJax.Hub, htmlEl[0]]);
} catch (err) {
console.log('HTML rendering error %o', err);
}
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
};
$scope.renderAngular = function() {
var retryRenderer = function() {
if (angular.element('#p' + $scope.paragraph.id + '_angular').length) {
try {
angular.element('#p' + $scope.paragraph.id + '_angular').html($scope.paragraph.result.msg);
$compile(angular.element('#p' + $scope.paragraph.id + '_angular').contents())(paragraphScope);
} catch (err) {
console.log('ANGULAR rendering error %o', err);
}
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
};
$scope.renderText = function() {
var retryRenderer = function() {
var textEl = angular.element('#p' + $scope.paragraph.id + '_text');
if (textEl.length) {
// clear all lines before render
$scope.clearTextOutput();
if ($scope.paragraph.result && $scope.paragraph.result.msg) {
$scope.appendTextOutput($scope.paragraph.result.msg);
}
angular.element('#p' + $scope.paragraph.id + '_text').bind('mousewheel', function(e) {
$scope.keepScrollDown = false;
});
$scope.flushStreamingOutput = true;
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
};
$scope.clearTextOutput = function() {
var textEl = angular.element('#p' + $scope.paragraph.id + '_text');
if (textEl.length) {
textEl.children().remove();
}
};
$scope.appendTextOutput = function(msg) {
var textEl = angular.element('#p' + $scope.paragraph.id + '_text');
if (textEl.length) {
var lines = msg.split('\n');
for (var i = 0; i < lines.length; i++) {
textEl.append(angular.element('<div></div>').text(lines[i]));
}
}
if ($scope.keepScrollDown) {
var doc = angular.element('#p' + $scope.paragraph.id + '_text');
doc[0].scrollTop = doc[0].scrollHeight;
}
};
var initializeDefault = function() {
var config = $scope.paragraph.config;
@ -310,37 +135,6 @@
config.colWidth = 12;
}
if (!config.graph) {
config.graph = {};
}
if (!config.graph.mode) {
config.graph.mode = 'table';
}
if (!config.graph.height) {
config.graph.height = 300;
}
if (!config.graph.optionOpen) {
config.graph.optionOpen = false;
}
if (!config.graph.keys) {
config.graph.keys = [];
}
if (!config.graph.values) {
config.graph.values = [];
}
if (!config.graph.groups) {
config.graph.groups = [];
}
if (!config.graph.scatter) {
config.graph.scatter = {};
}
if (config.enabled === undefined) {
config.enabled = true;
@ -1003,271 +797,10 @@
return cell;
};
$scope.setGraphMode = function(type, emit, refresh) {
if (emit) {
setNewMode(type);
} else {
clearUnknownColsFromGraphOption();
// set graph height
var height = $scope.paragraph.config.graph.height;
var graphContainerEl = angular.element('#p' + $scope.paragraph.id + '_graph');
graphContainerEl.height(height);
if (!type) {
type = 'table';
}
var builtInViz = builtInVisualizations[type];
if (builtInViz) {
// deactive previsouly active visualization
for (var t in builtInVisualizations) {
var v = builtInVisualizations[t].instance;
if (t !== type && v && v.isActive()) {
v.deactivate();
break;
}
}
if (!builtInViz.instance) { // not instantiated yet
// render when targetEl is available
var retryRenderer = function() {
var targetEl = angular.element('#p' + $scope.paragraph.id + '_' + type);
if (targetEl.length) {
try {
// set height
targetEl.height(height);
// instantiate visualization
var Visualization = builtInViz.class;
builtInViz.instance = new Visualization(targetEl, $scope.paragraph.config.graph);
builtInViz.instance.render(tableData);
builtInViz.instance.activate();
angular.element(window).resize(function() {
builtInViz.instance.resize();
});
} catch (err) {
console.log('Graph drawing error %o', err);
}
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
} else if (refresh) {
console.log('Refresh data');
// when graph options or data are changed
var retryRenderer = function() {
var targetEl = angular.element('#p' + $scope.paragraph.id + '_' + type);
if (targetEl.length) {
targetEl.height(height);
builtInViz.instance.setConfig($scope.paragraph.config.graph);
builtInViz.instance.render(tableData);
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
} else {
var retryRenderer = function() {
var targetEl = angular.element('#p' + $scope.paragraph.id + '_' + type);
if (targetEl.length) {
targetEl.height(height);
builtInViz.instance.activate();
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
}
}
}
};
var setNewMode = function(newMode) {
var newConfig = angular.copy($scope.paragraph.config);
var newParams = angular.copy($scope.paragraph.settings.params);
// graph options
newConfig.graph.mode = newMode;
// see switchApp()
_.set(newConfig, 'helium.activeApp', undefined);
commitParagraph($scope.paragraph.title, $scope.paragraph.text, newConfig, newParams);
};
var commitParagraph = function(title, text, config, params) {
websocketMsgSrv.commitParagraph($scope.paragraph.id, title, text, config, params);
};
$scope.isGraphMode = function(graphName) {
var activeAppId = _.get($scope.paragraph.config, 'helium.activeApp');
if ($scope.getResultType() === 'TABLE' && $scope.getGraphMode() === graphName && !activeAppId) {
return true;
} else {
return false;
}
};
$scope.onGraphOptionChange = function() {
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
$scope.removeGraphOptionKeys = function(idx) {
$scope.paragraph.config.graph.keys.splice(idx, 1);
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
$scope.removeGraphOptionValues = function(idx) {
$scope.paragraph.config.graph.values.splice(idx, 1);
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
$scope.removeGraphOptionGroups = function(idx) {
$scope.paragraph.config.graph.groups.splice(idx, 1);
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
$scope.setGraphOptionValueAggr = function(idx, aggr) {
$scope.paragraph.config.graph.values[idx].aggr = aggr;
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
$scope.removeScatterOptionXaxis = function(idx) {
$scope.paragraph.config.graph.scatter.xAxis = null;
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
$scope.removeScatterOptionYaxis = function(idx) {
$scope.paragraph.config.graph.scatter.yAxis = null;
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
$scope.removeScatterOptionGroup = function(idx) {
$scope.paragraph.config.graph.scatter.group = null;
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
$scope.removeScatterOptionSize = function(idx) {
$scope.paragraph.config.graph.scatter.size = null;
clearUnknownColsFromGraphOption();
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
};
/* Clear unknown columns from graph option */
var clearUnknownColsFromGraphOption = function() {
var unique = function(list) {
for (var i = 0; i < list.length; i++) {
for (var j = i + 1; j < list.length; j++) {
if (angular.equals(list[i], list[j])) {
list.splice(j, 1);
}
}
}
};
var removeUnknown = function(list) {
for (var i = 0; i < list.length; i++) {
// remove non existing column
var found = false;
for (var j = 0; j < tableData.columns.length; j++) {
var a = list[i];
var b = tableData.columns[j];
if (a.index === b.index && a.name === b.name) {
found = true;
break;
}
}
if (!found) {
list.splice(i, 1);
}
}
};
var removeUnknownFromFields = function(fields) {
for (var f in fields) {
if (fields[f]) {
var found = false;
for (var i = 0; i < tableData.columns.length; i++) {
var a = fields[f];
var b = tableData.columns[i];
if (a.index === b.index && a.name === b.name) {
found = true;
break;
}
}
if (!found && (fields[f] instanceof Object) && !(fields[f] instanceof Array)) {
fields[f] = null;
}
}
}
};
unique($scope.paragraph.config.graph.keys);
removeUnknown($scope.paragraph.config.graph.keys);
removeUnknown($scope.paragraph.config.graph.values);
unique($scope.paragraph.config.graph.groups);
removeUnknown($scope.paragraph.config.graph.groups);
removeUnknownFromFields($scope.paragraph.config.graph.scatter);
};
/* select default key and value if there're none selected */
var selectDefaultColsForGraphOption = function() {
if ($scope.paragraph.config.graph.keys.length === 0 && tableData.columns.length > 0) {
$scope.paragraph.config.graph.keys.push(tableData.columns[0]);
}
if ($scope.paragraph.config.graph.values.length === 0 && tableData.columns.length > 1) {
$scope.paragraph.config.graph.values.push(tableData.columns[1]);
}
if (!$scope.paragraph.config.graph.scatter.xAxis && !$scope.paragraph.config.graph.scatter.yAxis) {
if (tableData.columns.length > 1) {
$scope.paragraph.config.graph.scatter.xAxis = tableData.columns[0];
$scope.paragraph.config.graph.scatter.yAxis = tableData.columns[1];
} else if (tableData.columns.length === 1) {
$scope.paragraph.config.graph.scatter.xAxis = tableData.columns[0];
}
}
};
$scope.isValidSizeOption = function(options) {
var builtInViz = builtInVisualizations.scatterChart;
if (builtInViz && builtInViz.instance) {
return builtInViz.instance.isValidSizeOption(options);
} else {
return false;
}
};
$scope.resizeParagraph = function(width, height) {
$scope.changeColWidth(width);
$timeout(function() {
autoAdjustEditorHeight($scope.paragraph.id + '_editor');
$scope.changeHeight(height);
}, 200);
};
$scope.changeHeight = function(height) {
var newParams = angular.copy($scope.paragraph.settings.params);
var newConfig = angular.copy($scope.paragraph.config);
newConfig.graph.height = height;
commitParagraph($scope.paragraph.title, $scope.paragraph.text, newConfig, newParams);
};
/** Utility function */
if (typeof String.prototype.startsWith !== 'function') {
@ -1311,33 +844,6 @@
$scope.keepScrollDown = false;
};
$scope.exportToDSV = function(delimiter) {
var dsv = '';
for (var titleIndex in tableData.columns) {
dsv += tableData.columns[titleIndex].name + delimiter;
}
dsv = dsv.substring(0, dsv.length - 1) + '\n';
for (var r in tableData.rows) {
var row = tableData.rows[r];
var dsvRow = '';
for (var index in row) {
var stringValue = (row[index]).toString();
if (stringValue.contains(delimiter)) {
dsvRow += '"' + stringValue + '"' + delimiter;
} else {
dsvRow += row[index] + delimiter;
}
}
dsv += dsvRow.substring(0, dsvRow.length - 1) + '\n';
}
var extension = '';
if (delimiter === '\t') {
extension = 'tsv';
} else if (delimiter === ',') {
extension = 'csv';
}
saveAsService.saveAs(dsv, 'data', extension);
};
// Helium ---------------------------------------------
@ -1648,22 +1154,10 @@
!angular.equals(data.paragraph.settings, $scope.paragraph.settings) ||
!angular.equals(data.paragraph.config, $scope.paragraph.config))
) {
var oldType = $scope.getResultType();
var newType = $scope.getResultType(data.paragraph);
var oldGraphMode = $scope.getGraphMode();
var newGraphMode = $scope.getGraphMode(data.paragraph);
var oldActiveApp = _.get($scope.paragraph.config, 'helium.activeApp');
var newActiveApp = _.get(data.paragraph.config, 'helium.activeApp');
var statusChanged = (data.paragraph.status !== $scope.paragraph.status);
var resultRefreshed = (data.paragraph.dateFinished !== $scope.paragraph.dateFinished) ||
isEmpty(data.paragraph.result) !== isEmpty($scope.paragraph.result) ||
data.paragraph.status === 'ERROR' || (data.paragraph.status === 'FINISHED' && statusChanged) ||
(!newActiveApp && oldActiveApp !== newActiveApp);
//console.log("updateParagraph oldData %o, newData %o. type %o -> %o, mode %o -> %o", $scope.paragraph, data, oldType, newType, oldGraphMode, newGraphMode);
data.paragraph.status === 'ERROR' || (data.paragraph.status === 'FINISHED' && statusChanged);
if ($scope.paragraph.text !== data.paragraph.text) {
if ($scope.dirtyText) { // check if editor has local update
@ -1680,6 +1174,20 @@
}
}
/** broadcast update to result controller **/
if (data.paragraph.result) {
for (var i in data.paragraph.result.msg) {
var newResult = data.paragraph.result.msg[i];
var oldResult = $scope.paragraph.result.msg[i];
var newConfig = data.paragraph.config.result ? data.paragraph.config.result[i] : {};
var oldConfig = $scope.paragraph.config.result ? $scope.paragraph.config.result[i] : {};
if (!angular.equals(newResult, oldResult) ||
!angular.equals(newConfig, oldConfig)) {
$rootScope.$broadcast('updateResult', newResult, newConfig, data.paragraph, parseInt(i));
}
}
}
/** push the rest */
$scope.paragraph.aborted = data.paragraph.aborted;
$scope.paragraph.user = data.paragraph.user;
@ -1705,38 +1213,6 @@
$scope.paragraph.config = data.paragraph.config;
}
if (newType === 'TABLE') {
if (oldType !== 'TABLE' || resultRefreshed) {
var TableData = zeppelin.TableData;
tableData = new TableData();
tableData.loadParagraphResult($scope.paragraph.result);
$scope.tableDataColumns = tableData.columns;
$scope.tableDataComment = tableData.comment;
clearUnknownColsFromGraphOption();
selectDefaultColsForGraphOption();
}
/** User changed the chart type? */
if (oldGraphMode !== newGraphMode) {
$scope.setGraphMode(newGraphMode, false, false);
} else {
$scope.setGraphMode(newGraphMode, false, true);
}
} else if (newType === 'HTML' && resultRefreshed) {
$scope.renderHtml();
} else if (newType === 'ANGULAR' && resultRefreshed) {
$scope.renderAngular();
} else if (newType === 'TEXT' && resultRefreshed) {
$scope.renderText();
}
getApplicationStates();
getSuggestions();
if (newActiveApp && newActiveApp !== oldActiveApp) {
var app = _.find($scope.apps, {id: newActiveApp});
renderApp(app);
}
if (statusChanged || resultRefreshed) {
// when last paragraph runs, zeppelin automatically appends new paragraph.
// this broadcast will focus to the newly inserted paragraph

View file

@ -55,9 +55,9 @@ limitations under the License.
<div class="tableDisplay"
ng-show="!paragraph.config.tableHide"
ng-controller="ResultCtrl"
ng-repeat="result in paragraph.result"
ng-init="init(result)"
ng-include src="'app/notebook/paragraph/result.html'"
ng-repeat="result in paragraph.result.msg track by $index"
ng-init="init(result, paragraph.config.result[$index], paragraph, $index)"
ng-include src="'app/notebook/paragraph/result/result.html'"
>
</div>
</div>

View file

@ -12,23 +12,22 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<div id="{{paragraph.id}}_switch"
ng-if="(paragraph.result.type == 'TABLE' || apps.length > 0 || suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
<div id="{{id}}_switch"
ng-if="(type == 'TABLE' || apps.length > 0 || suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
class="btn-group"
style="margin-bottom: 10px;">
<button type="button" class="btn btn-default btn-sm"
ng-if="paragraph.result.type == 'TABLE'"
ng-if="type == 'TABLE'"
ng-repeat="viz in builtInTableDataVisualizationList track by $index"
ng-class="{'active' : isGraphMode(viz.id)}"
ng-click="setGraphMode(viz.id, true, false)"
tooltip="{{viz.name}}" tooltip-placement="bottom"><i ng-class="viz.icon"></i>
ng-class="{'active' : viz.id == graphMode}"
ng-click="switchViz(viz.id)"><i ng-class="viz.icon"></i>
</button>
<button type="button"
ng-if="paragraph.result.type != 'TABLE'"
ng-if="type != 'TABLE'"
ng-click="switchApp()"
ng-class="{'active' : !paragraph.config.helium.activeApp}"
ng-class="{'active' : !config.helium.activeApp}"
class="btn btn-default btn-sm"><i class="fa fa-terminal"></i>
</button>
@ -36,7 +35,7 @@ limitations under the License.
class="btn btn-default btn-sm"
ng-repeat="app in apps"
ng-click="switchApp(app.id)"
ng-class="{'active' : app.id == paragraph.config.helium.activeApp}"
ng-class="{'active' : app.id == config.helium.activeApp}"
ng-bind-html="app.pkg.icon">
</button>
</div>
@ -71,7 +70,7 @@ limitations under the License.
</div>
<div class="btn-group"
ng-if="paragraph.result.type == 'TABLE' && !asIframe && !viewOnly"
ng-if="type == 'TABLE' && !asIframe && !viewOnly"
style="margin-bottom: 10px;">
<button type="button" class="btn btn-default btn-sm"
style="margin-left:10px"
@ -91,9 +90,9 @@ limitations under the License.
</div>
<span
ng-if="getResultType()=='TABLE' && !paragraph.config.helium.activeApp && getGraphMode()!='table' && !asIframe && !viewOnly"
ng-if="type=='TABLE' && !paragraph.config.helium.activeApp && graphMode!='table' && !asIframe && !viewOnly"
style="margin-left:10px; cursor:pointer; display: inline-block; vertical-align:top; position: relative; line-height:30px;">
<a class="btnText" ng-click="toggleGraphOption()">
settings <span ng-class="paragraph.config.graph.optionOpen ? 'fa fa-caret-up' : 'fa fa-caret-down'"></span>
<a class="btnText" ng-click="toggleGraphSetting()">
settings <span ng-class="config.graph.optionOpen ? 'fa fa-caret-up' : 'fa fa-caret-down'"></span>
</a>
</span>

View file

@ -11,14 +11,13 @@ 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="p{{paragraph.id}}_graph"
<div id="p{{id}}_graph"
class="graphContainer"
ng-class="{'noOverflow': getGraphMode()=='table'}"
ng-show="getResultType()=='TABLE'"
ng-class="{'noOverflow': graphMode=='table'}"
ng-show="type =='TABLE'"
>
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
id="p{{paragraph.id}}_{{viz.id}}"
ng-show="getGraphMode()==viz.id">
id="p{{id}}_{{viz.id}}"
ng-show="graphMode == viz.id">
</div>
</div>

View file

@ -12,10 +12,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<div>
<div ng-if="isGraphMode('lineChart') || isGraphMode('lineWithFocusChart')">
<div ng-if="graphMode=='lineChart' || graphMode=='lineWithFocusChart'">
<label>
<input type="checkbox"
ng-model="paragraph.config.graph.forceY"
ng-model="config.graph.forceY"
ng-click="onGraphOptionChange()" />
force Y to 0
</label>
@ -23,24 +23,9 @@ limitations under the License.
<label>
<input type="checkbox"
ng-model="paragraph.config.graph.lineWithFocus"
ng-model="config.graph.lineWithFocus"
ng-click="onGraphOptionChange()" />
show line chart with focus
</label>
</div>
<div ng-if="isGraphMode('map')">
<label>Basemap</label>
<span class="dropdown">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" style="min-width: 0px;" data-toggle="dropdown">
<span ng-bind="paragraph.config.graph.map.baseMapType"></span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" style="min-width: 70px;">
<li ng-repeat="opt in baseMapOption">
<a ng-click="setMapBaseMap(opt)">{{opt}}</a>
</li>
</ul>
</span>
<br/>
</div>
</div>

View file

@ -12,10 +12,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="option lightBold" style="overflow: visible;"
ng-if="getResultType()=='TABLE' && getGraphMode()!='table'
&& paragraph.config.graph.optionOpen && !asIframe && !viewOnly">
ng-if="type=='TABLE' && graphMode!='table'
&& config.graph.optionOpen && !asIframe && !viewOnly">
<div ng-include src="'app/notebook/paragraph/paragraph-graphOptions.html'"></div>
<div ng-include src="'app/notebook/paragraph/result/result-graphOptions.html'"></div>
All fields:
<div class="allFields row">
@ -32,15 +32,15 @@ limitations under the License.
</ul>
</div>
<div class="row" ng-if="getGraphMode()!='scatterChart'">
<div class="row" ng-if="graphMode!='scatterChart'">
<div class="col-md-4">
<span class="columns lightBold">
Keys
<ul data-drop="true"
ng-model="paragraph.config.graph.keys"
ng-model="config.graph.keys"
jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
class="list-unstyled">
<li ng-repeat="item in paragraph.config.graph.keys">
<li ng-repeat="item in config.graph.keys">
<div class="btn btn-primary btn-xs">
{{item.name}} <span class="fa fa-close" ng-click="removeGraphOptionKeys($index)"></span>
</div>
@ -52,10 +52,10 @@ limitations under the License.
<span class="columns lightBold">
Groups
<ul data-drop="true"
ng-model="paragraph.config.graph.groups"
ng-model="config.graph.groups"
jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
class="list-unstyled">
<li ng-repeat="item in paragraph.config.graph.groups">
<li ng-repeat="item in config.graph.groups">
<div class="btn btn-success btn-xs">
{{item.name}} <span class="fa fa-close" ng-click="removeGraphOptionGroups($index)"></span>
</div>
@ -67,10 +67,10 @@ limitations under the License.
<span class="columns lightBold">
Values
<ul data-drop="true"
ng-model="paragraph.config.graph.values"
ng-model="config.graph.values"
jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
class="list-unstyled">
<li ng-repeat="item in paragraph.config.graph.values">
<li ng-repeat="item in config.graph.values">
<div class="btn-group">
<div class="btn btn-info btn-xs dropdown-toggle"
type="button"
@ -93,18 +93,18 @@ limitations under the License.
</div>
</div>
<div class="row" ng-if="getGraphMode()=='scatterChart'">
<div class="row" ng-if="graphMode=='scatterChart'">
<div class="col-md-3">
<span class="columns lightBold">
xAxis
<ul data-drop="true"
ng-model="paragraph.config.graph.scatter.xAxis"
ng-model="config.graph.scatter.xAxis"
jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
class="list-unstyled"
style="height:36px">
<li ng-if="paragraph.config.graph.scatter.xAxis">
<li ng-if="config.graph.scatter.xAxis">
<div class="btn btn-primary btn-xs">
{{paragraph.config.graph.scatter.xAxis.name}} <span class="fa fa-close" ng-click="removeScatterOptionXaxis($index)"></span>
{{config.graph.scatter.xAxis.name}} <span class="fa fa-close" ng-click="removeScatterOptionXaxis($index)"></span>
</div>
</li>
</ul>
@ -114,13 +114,13 @@ limitations under the License.
<span class="columns lightBold">
yAxis
<ul data-drop="true"
ng-model="paragraph.config.graph.scatter.yAxis"
ng-model="config.graph.scatter.yAxis"
jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
class="list-unstyled"
style="height:36px">
<li ng-if="paragraph.config.graph.scatter.yAxis">
<li ng-if="config.graph.scatter.yAxis">
<div class="btn btn-success btn-xs">
{{paragraph.config.graph.scatter.yAxis.name}} <span class="fa fa-close" ng-click="removeScatterOptionYaxis($index)"></span>
{{config.graph.scatter.yAxis.name}} <span class="fa fa-close" ng-click="removeScatterOptionYaxis($index)"></span>
</div>
</li>
</ul>
@ -130,13 +130,13 @@ limitations under the License.
<span class="columns lightBold">
group
<ul data-drop="true"
ng-model="paragraph.config.graph.scatter.group"
ng-model="config.graph.scatter.group"
jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
class="list-unstyled"
style="height:36px">
<li ng-if="paragraph.config.graph.scatter.group">
<li ng-if="config.graph.scatter.group">
<div class="btn btn-info btn-xs">
{{paragraph.config.graph.scatter.group.name}} <span class="fa fa-close" ng-click="removeScatterOptionGroup($index)"></span>
{{config.graph.scatter.group.name}} <span class="fa fa-close" ng-click="removeScatterOptionGroup($index)"></span>
</div>
</li>
</ul>
@ -152,13 +152,13 @@ limitations under the License.
<li>Zeppelin consider values as discrete when the values contain string value or the number of distinct values are bigger than 5% of total number of values.</li>
<li>Size field button turns to grey when the option you chose is not valid.</li>"></a>
<ul data-drop="true"
ng-model="paragraph.config.graph.scatter.size"
ng-model="config.graph.scatter.size"
jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
class="list-unstyled"
style="height:36px">
<li ng-if="paragraph.config.graph.scatter.size">
<div class="btn btn-xs" style="color:white" ng-class="{'btn-warning': isValidSizeOption(paragraph.config.graph.scatter, paragraph.result.rows)}">
{{paragraph.config.graph.scatter.size.name}} <span class="fa fa-close" ng-click="removeScatterOptionSize($index)"></span>
<li ng-if="config.graph.scatter.size">
<div class="btn btn-xs" style="color:white" ng-class="{'btn-warning': isValidSizeOption(config.graph.scatter)}">
{{config.graph.scatter.size.name}} <span class="fa fa-close" ng-click="removeScatterOptionSize($index)"></span>
</div>
</li>
</ul>

View file

@ -12,27 +12,27 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<div
id="p{{paragraph.id}}_resize"
ng-if="!paragraph.config.helium.activeApp"
id="p{{id}}_resize"
ng-if="!config.helium.activeApp"
style="padding-bottom: 5px;"
resize='{"allowresize": "{{!asIframe && !viewOnly}}", "graphType": "{{getResultType()}}"}'
resize='{"allowresize": "{{!asIframe && !viewOnly}}", "graphType": "{{type}}"}'
resizable on-resize="resizeParagraph(width, height);">
<div ng-include src="'app/notebook/paragraph/paragraph-graph.html'"></div>
<div ng-include src="'app/notebook/paragraph/result/result-graph.html'"></div>
<div id="{{paragraph.id}}_comment"
<div id="{{id}}_comment"
class="text"
ng-if="getResultType()=='TABLE' && tableDataComment"
ng-if="type == 'TABLE' && tableDataComment"
ng-bind-html="tableDataComment">
</div>
<div id="{{paragraph.id}}_text"
ng-if="getResultType() == 'TEXT'">
<div id="p{{id}}_text"
ng-if="type == 'TEXT'">
<div class="fa fa-level-down scroll-paragraph-down"
ng-show="showScrollDownIcon()"
ng-click="scrollParagraphDown()"
tooltip="Follow Output"></div>
<div id="p{{paragraph.id}}_text"
style="max-height: {{paragraph.config.graph.height}}px; overflow: auto"
<div id="p{{id}}_text"
style="max-height: {{config.graph.height}}px; overflow: auto"
class="text"></div>
<div class="fa fa-chevron-up scroll-paragraph-up"
ng-show="showScrollUpIcon()"
@ -40,21 +40,21 @@ limitations under the License.
tooltip="Scroll Top"></div>
</div>
<div id="p{{paragraph.id}}_html"
<div id="p{{id}}_html"
class="resultContained"
ng-if="getResultType() == 'HTML'">
ng-if="type == 'HTML'">
</div>
<div id="p{{paragraph.id}}_angular"
<div id="p{{id}}_angular"
class="resultContained"
ng-if="getResultType() == 'ANGULAR'">
ng-if="type == 'ANGULAR'">
</div>
<img id="{{paragraph.id}}_img"
ng-if="getResultType() == 'IMG'"
ng-src="{{getBase64ImageSrc(paragraph.result.msg)}}" />
<img id="{{id}}_img"
ng-if="type == 'IMG'"
ng-src="{{getBase64ImageSrc(result.data)}}" />
<div id="{{paragraph.id}}_error"
<div id="{{id}}_error"
class="error text"
ng-if="paragraph.status == 'ERROR'"
ng-bind="paragraph.errorMessage">

View file

@ -12,6 +12,7 @@
* limitations under the License.
*/
'use strict';
(function() {
angular.module('zeppelinWebApp').controller('ResultCtrl', ResultCtrl);
@ -37,13 +38,577 @@
$timeout, $compile, $http, $q, websocketMsgSrv,
baseUrlSrv, ngToast, saveAsService) {
$scope.parentNote = null;
$scope.paragraph = null;
$scope.originalText = '';
$scope.editor = null;
/**
* Built-in visualizations
*/
$scope.builtInTableDataVisualizationList = [
{
id: 'table', // paragraph.config.graph.mode
name: 'Table', // human readable name. tooltip
icon: 'fa fa-table'
},
{
id: 'multiBarChart',
name: 'Bar Chart',
icon: 'fa fa-bar-chart',
transformation: 'pivot'
},
{
id: 'pieChart',
name: 'Pie Chart',
icon: 'fa fa-pie-chart',
transformation: 'pivot'
},
{
id: 'stackedAreaChart',
name: 'Area Chart',
icon: 'fa fa-area-chart',
transformation: 'pivot'
},
{
id: 'lineChart',
name: 'Line Chart',
icon: 'fa fa-line-chart',
transformation: 'pivot'
},
{
id: 'scatterChart',
name: 'Scatter Chart',
icon: 'cf cf-scatter-chart'
}
];
$scope.init = function(results, configs) {
console.log('result controller init %o %o', results, configs);
/**
* Holds class and actual runtime instance and related infos of built-in visualizations
*/
var builtInVisualizations = {
'table': {
class: zeppelin.TableVisualization,
instance: undefined // created from setGraphMode()
},
'multiBarChart': {
class: zeppelin.BarchartVisualization,
instance: undefined
},
'pieChart': {
class: zeppelin.PiechartVisualization,
instance: undefined
},
'stackedAreaChart': {
class: zeppelin.AreachartVisualization,
instance: undefined
},
'lineChart': {
class: zeppelin.LinechartVisualization,
instance: undefined
},
'scatterChart': {
class: zeppelin.ScatterchartVisualization,
instance: undefined
}
};
// type
$scope.type;
// Data of the result
var data;
// config
$scope.config;
// resultId = paragraph.id + index
$scope.id;
// referece to paragraph
var paragraph;
// index of the result
var resultIndex
// TableData instance
var tableData;
// available columns in tabledata
$scope.tableDataColumns = [];
// graphMode
$scope.graphMode;
$scope.init = function(result, config, paragraph, index) {
console.log('result controller init %o %o %o', result, config, index);
updateData(result, config, paragraph, index);
renderResult($scope.type);
};
$scope.$on('updateResult', function(event, result, newConfig, paragraphRef, index) {
if (paragraph.id !== paragraphRef.id || index !== resultIndex) {
return;
}
console.log('updateResult %o %o %o %o', result, newConfig, paragraphRef, index);
/*
var oldType = $scope.getResultType();
var newType = $scope.getResultType(data.paragraph);
var oldGraphMode = $scope.getGraphMode();
var newGraphMode = $scope.getGraphMode(data.paragraph);
var oldActiveApp = _.get($scope.paragraph.config, 'helium.activeApp');
var newActiveApp = _.get(data.paragraph.config, 'helium.activeApp');
*/
updateData(result, newConfig, paragraph, resultIndex);
renderResult($scope.type, true);
});
var updateData = function(result, config, paragraphRef, index) {
data = result.data;
paragraph = paragraphRef;
resultIndex = parseInt(index);
$scope.id = paragraph.id + "_" + index;
$scope.type = result.type;
config = config ? config : {};
// initialize default config values
if (!config.graph) {
config.graph = {};
}
if (!config.graph.mode) {
config.graph.mode = 'table';
}
if (!config.graph.height) {
config.graph.height = 300;
}
if (!config.graph.optionOpen) {
config.graph.optionOpen = false;
}
if (!config.graph.keys) {
config.graph.keys = [];
}
if (!config.graph.values) {
config.graph.values = [];
}
if (!config.graph.groups) {
config.graph.groups = [];
}
if (!config.graph.scatter) {
config.graph.scatter = {};
}
$scope.graphMode = config.graph.mode;
$scope.config = angular.copy(config);
if ($scope.type === 'TABLE') {
var TableData = zeppelin.TableData;
tableData = new TableData();
tableData.loadParagraphResult({type: $scope.type, msg: data});
$scope.tableDataColumns = tableData.columns;
$scope.tableDataComment = tableData.comment;
}
};
var renderResult = function(type, refresh) {
if (type === 'TABLE') {
$scope.renderGraph($scope.graphMode, refresh);
} else if (type === 'HTML') {
renderHtml();
} else if (type === 'ANGULAR') {
renderAngular();
} else if (type === 'TEXT') {
renderText();
}
/*
getApplicationStates();
getSuggestions();
if (newActiveApp && newActiveApp !== oldActiveApp) {
var app = _.find($scope.apps, {id: newActiveApp});
renderApp(app);
}
*/
};
var renderHtml = function() {
var retryRenderer = function() {
var htmlEl = angular.element('#p' + $scope.id + '_html');
if (htmlEl.length) {
try {
htmlEl.html(data);
htmlEl.find('pre code').each(function(i, e) {
hljs.highlightBlock(e);
});
/*eslint new-cap: [2, {"capIsNewExceptions": ["MathJax.Hub.Queue"]}]*/
MathJax.Hub.Queue(['Typeset', MathJax.Hub, htmlEl[0]]);
} catch (err) {
console.log('HTML rendering error %o', err);
}
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
};
var renderAngular = function() {
var retryRenderer = function() {
if (angular.element('#p' + $scope.id + '_angular').length) {
try {
angular.element('#p' + $scope.id + '_angular').html(data);
$compile(angular.element('#p' + $scope.id + '_angular').contents())(paragraphScope);
} catch (err) {
console.log('ANGULAR rendering error %o', err);
}
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
};
var renderText = function() {
var retryRenderer = function() {
var textEl = angular.element('#p' + $scope.id + '_text');
if (textEl.length) {
// clear all lines before render
clearTextOutput();
if (data) {
appendTextOutput(data);
}
angular.element('#p' + $scope.id + '_text').bind('mousewheel', function(e) {
$scope.keepScrollDown = false;
});
$scope.flushStreamingOutput = true;
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
};
var clearTextOutput = function() {
var textEl = angular.element('#p' + $scope.id + '_text');
if (textEl.length) {
textEl.children().remove();
}
};
var appendTextOutput = function(msg) {
var textEl = angular.element('#p' + $scope.id + '_text');
if (textEl.length) {
var lines = msg.split('\n');
for (var i = 0; i < lines.length; i++) {
textEl.append(angular.element('<div></div>').text(lines[i]));
}
}
if ($scope.keepScrollDown) {
var doc = angular.element('#p' + $scope.id + '_text');
doc[0].scrollTop = doc[0].scrollHeight;
}
};
$scope.renderGraph = function(type, refresh) {
clearUnknownColsFromGraphOption();
// set graph height
var height = $scope.config.graph.height;
var graphContainerEl = angular.element('#p' + $scope.id + '_graph');
graphContainerEl.height(height);
if (!type) {
type = 'table';
}
var builtInViz = builtInVisualizations[type];
if (builtInViz) {
// deactive previsouly active visualization
for (var t in builtInVisualizations) {
var v = builtInVisualizations[t].instance;
if (t !== type && v && v.isActive()) {
v.deactivate();
break;
}
}
if (!builtInViz.instance) { // not instantiated yet
// render when targetEl is available
var retryRenderer = function() {
var targetEl = angular.element('#p' + $scope.id + '_' + type);
if (targetEl.length) {
try {
// set height
targetEl.height(height);
// instantiate visualization
var Visualization = builtInViz.class;
builtInViz.instance = new Visualization(targetEl, $scope.config.graph);
builtInViz.instance.render(tableData);
builtInViz.instance.activate();
angular.element(window).resize(function() {
builtInViz.instance.resize();
});
} catch (err) {
console.log('Graph drawing error %o', err);
}
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
} else if (refresh) {
console.log('Refresh data %o', tableData);
// when graph options or data are changed
var retryRenderer = function() {
var targetEl = angular.element('#p' + $scope.id + '_' + type);
if (targetEl.length) {
targetEl.height(height);
builtInViz.instance.setConfig($scope.config.graph);
builtInViz.instance.render(tableData);
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
} else {
var retryRenderer = function() {
var targetEl = angular.element('#p' + $scope.id + '_' + type);
if (targetEl.length) {
targetEl.height(height);
builtInViz.instance.activate();
} else {
$timeout(retryRenderer, 10);
}
};
$timeout(retryRenderer);
}
}
};
$scope.switchViz = function(newMode) {
var newConfig = angular.copy($scope.config);
var newParams = angular.copy(paragraph.settings.params);
// graph options
newConfig.graph.mode = newMode;
// see switchApp()
_.set(newConfig, 'helium.activeApp', undefined);
commitParagraphResult(paragraph.title, paragraph.text, newConfig, newParams);
};
var commitParagraphResult = function(title, text, config, params) {
var newParagraphConfig = angular.copy(paragraph.config);
newParagraphConfig.result = newParagraphConfig.result || [];
newParagraphConfig.result[resultIndex] = config;
websocketMsgSrv.commitParagraph(paragraph.id, title, text, newParagraphConfig, params);
};
$scope.toggleGraphSetting = function() {
var newConfig = angular.copy($scope.config);
if (newConfig.graph.optionOpen) {
newConfig.graph.optionOpen = false;
} else {
newConfig.graph.optionOpen = true;
}
var newParams = angular.copy(paragraph.settings.params);
commitParagraphResult(paragraph.title, paragraph.text, newConfig, newParams);
};
var commitConfigChange = function(config) {
clearUnknownColsFromGraphOption();
var newConfig = angular.copy(config);
var newParams = angular.copy(paragraph.settings.params);
commitParagraphResult(paragraph.title, paragraph.text, newConfig, newParams);
};
$scope.onGraphOptionChange = function() {
commitConfigChange($scope.config);
};
$scope.removeGraphOptionKeys = function(idx) {
$scope.config.graph.keys.splice(idx, 1);
commitConfigChange($scope.config);
};
$scope.removeGraphOptionValues = function(idx) {
$scope.config.graph.values.splice(idx, 1);
commitConfigChange($scope.config);
};
$scope.removeGraphOptionGroups = function(idx) {
$scope.config.graph.groups.splice(idx, 1);
commitConfigChange($scope.config);
};
$scope.setGraphOptionValueAggr = function(idx, aggr) {
$scope.config.graph.values[idx].aggr = aggr;
commitConfigChange($scope.config);
};
$scope.removeScatterOptionXaxis = function(idx) {
$scope.config.graph.scatter.xAxis = null;
commitConfigChange($scope.config);
};
$scope.removeScatterOptionYaxis = function(idx) {
$scope.config.graph.scatter.yAxis = null;
commitConfigChange($scope.config);
};
$scope.removeScatterOptionGroup = function(idx) {
$scope.config.graph.scatter.group = null;
commitConfigChange($scope.config);
};
$scope.removeScatterOptionSize = function(idx) {
$scope.config.graph.scatter.size = null;
commitConfigChange($scope.config);
};
var clearUnknownColsFromGraphOption = function() {
var unique = function(list) {
for (var i = 0; i < list.length; i++) {
for (var j = i + 1; j < list.length; j++) {
if (angular.equals(list[i], list[j])) {
list.splice(j, 1);
}
}
}
};
var removeUnknown = function(list) {
for (var i = 0; i < list.length; i++) {
// remove non existing column
var found = false;
for (var j = 0; j < tableData.columns.length; j++) {
var a = list[i];
var b = tableData.columns[j];
if (a.index === b.index && a.name === b.name) {
found = true;
break;
}
}
if (!found) {
list.splice(i, 1);
}
}
};
var removeUnknownFromFields = function(fields) {
for (var f in fields) {
if (fields[f]) {
var found = false;
for (var i = 0; i < tableData.columns.length; i++) {
var a = fields[f];
var b = tableData.columns[i];
if (a.index === b.index && a.name === b.name) {
found = true;
break;
}
}
if (!found && (fields[f] instanceof Object) && !(fields[f] instanceof Array)) {
fields[f] = null;
}
}
}
};
unique($scope.paragraph.config.graph.keys);
removeUnknown($scope.paragraph.config.graph.keys);
removeUnknown($scope.paragraph.config.graph.values);
unique($scope.paragraph.config.graph.groups);
removeUnknown($scope.paragraph.config.graph.groups);
removeUnknownFromFields($scope.paragraph.config.graph.scatter);
};
/* select default key and value if there're none selected */
var selectDefaultColsForGraphOption = function() {
if ($scope.paragraph.config.graph.keys.length === 0 && tableData.columns.length > 0) {
$scope.paragraph.config.graph.keys.push(tableData.columns[0]);
}
if ($scope.paragraph.config.graph.values.length === 0 && tableData.columns.length > 1) {
$scope.paragraph.config.graph.values.push(tableData.columns[1]);
}
if (!$scope.paragraph.config.graph.scatter.xAxis && !$scope.paragraph.config.graph.scatter.yAxis) {
if (tableData.columns.length > 1) {
$scope.paragraph.config.graph.scatter.xAxis = tableData.columns[0];
$scope.paragraph.config.graph.scatter.yAxis = tableData.columns[1];
} else if (tableData.columns.length === 1) {
$scope.paragraph.config.graph.scatter.xAxis = tableData.columns[0];
}
}
};
$scope.isValidSizeOption = function(options) {
var builtInViz = builtInVisualizations.scatterChart;
if (builtInViz && builtInViz.instance) {
return builtInViz.instance.isValidSizeOption(options);
} else {
return false;
}
};
$scope.resizeParagraph = function(width, height) {
$scope.changeColWidth(width);
$timeout(function() {
autoAdjustEditorHeight($scope.paragraph.id + '_editor');
$scope.changeHeight(height);
}, 200);
};
$scope.changeHeight = function(height) {
var newParams = angular.copy($scope.paragraph.settings.params);
var newConfig = angular.copy($scope.paragraph.config);
newConfig.graph.height = height;
commitParagraph($scope.paragraph.title, $scope.paragraph.text, newConfig, newParams);
};
$scope.exportToDSV = function(delimiter) {
var dsv = '';
for (var titleIndex in tableData.columns) {
dsv += tableData.columns[titleIndex].name + delimiter;
}
dsv = dsv.substring(0, dsv.length - 1) + '\n';
for (var r in tableData.rows) {
var row = tableData.rows[r];
var dsvRow = '';
for (var index in row) {
var stringValue = (row[index]).toString();
if (stringValue.contains(delimiter)) {
dsvRow += '"' + stringValue + '"' + delimiter;
} else {
dsvRow += row[index] + delimiter;
}
}
dsv += dsvRow.substring(0, dsvRow.length - 1) + '\n';
}
var extension = '';
if (delimiter === '\t') {
extension = 'tsv';
} else if (delimiter === ',') {
extension = 'csv';
}
saveAsService.saveAs(dsv, 'data', extension);
};
};
})();

View file

@ -190,6 +190,7 @@ limitations under the License.
<script src="app/credential/credential.controller.js"></script>
<script src="app/configuration/configuration.controller.js"></script>
<script src="app/notebook/paragraph/paragraph.controller.js"></script>
<script src="app/notebook/paragraph/result/result.controller.js"></script>
<script src="app/search/result-list.controller.js"></script>
<script src="app/notebookRepos/notebookRepos.controller.js"></script>
<script src="components/arrayOrderingSrv/arrayOrdering.service.js"></script>

View file

@ -31,14 +31,11 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.StringUtils;
import org.apache.zeppelin.interpreter.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.display.AngularObject;
import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.display.Input;
import org.apache.zeppelin.interpreter.*;
import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
import org.apache.zeppelin.notebook.repo.NotebookRepo;

View file

@ -338,8 +338,7 @@ public class Paragraph extends Job implements Serializable, Cloneable {
context.out.flush();
List<InterpreterResultMessage> resultMessages = context.out.toInterpreterResultMessage();
resultMessages.addAll(ret.message());
return resultMessages;
return new InterpreterResult(ret.code(), resultMessages);
} finally {
InterpreterContext.remove();
}