|
|
@@ -277,373 +277,11 @@ from desktop.views import _ko
|
|
|
<script type="text/javascript" charset="utf-8">
|
|
|
(function (factory) {
|
|
|
if(typeof require === "function") {
|
|
|
- require(['knockout', 'desktop/js/assistHelper'], factory);
|
|
|
+ require(['knockout', 'desktop/js/assist/assistSource'], factory);
|
|
|
} else {
|
|
|
- factory(ko, AssistHelper);
|
|
|
+ factory(ko, AssistSource);
|
|
|
}
|
|
|
- }(function (ko, AssistHelper) {
|
|
|
- function AssistEntry (definition, parent, assistSource, filter) {
|
|
|
- var self = this;
|
|
|
- self.definition = definition;
|
|
|
-
|
|
|
- self.assistSource = assistSource;
|
|
|
- self.parent = parent;
|
|
|
- self.filter = filter;
|
|
|
-
|
|
|
- self.expandable = typeof definition.type === "undefined" || definition.type === "struct" || definition.type === "array" || definition.type === "map";
|
|
|
-
|
|
|
- self.loaded = false;
|
|
|
- self.loading = ko.observable(false);
|
|
|
- self.open = ko.observable(false);
|
|
|
- self.entries = ko.observableArray([]);
|
|
|
- self.statsVisible = ko.observable(false);
|
|
|
-
|
|
|
- self.open.subscribe(function(newValue) {
|
|
|
- if (newValue && self.entries().length == 0) {
|
|
|
- self.loadEntries();
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.hasEntries = ko.computed(function() {
|
|
|
- return self.entries().length > 0;
|
|
|
- });
|
|
|
-
|
|
|
- self.filteredEntries = ko.computed(function () {
|
|
|
- if (self.filter == null || self.filter().length === 0) {
|
|
|
- return self.entries();
|
|
|
- }
|
|
|
- var result = [];
|
|
|
- $.each(self.entries(), function (index, entry) {
|
|
|
- if (entry.definition.name.toLowerCase().indexOf(self.filter()) > -1) {
|
|
|
- result.push(entry);
|
|
|
- }
|
|
|
- });
|
|
|
- return result;
|
|
|
- });
|
|
|
-
|
|
|
- self.editorText = ko.computed(function () {
|
|
|
- if (self.definition.isTable) {
|
|
|
- return self.definition.name;
|
|
|
- }
|
|
|
- if (self.definition.isColumn) {
|
|
|
- return self.definition.name + ", ";
|
|
|
- }
|
|
|
- var parts = [];
|
|
|
- var entry = self;
|
|
|
- while (entry != null) {
|
|
|
- if (entry.definition.isTable) {
|
|
|
- break;
|
|
|
- }
|
|
|
- if (entry.definition.isArray || entry.definition.isMapValue) {
|
|
|
- if (self.assistSource.assistHelper.type === 'hive') {
|
|
|
- parts.push("[]");
|
|
|
- }
|
|
|
- } else {
|
|
|
- parts.push(entry.definition.name);
|
|
|
- parts.push(".");
|
|
|
- }
|
|
|
- entry = entry.parent;
|
|
|
- }
|
|
|
- parts.reverse();
|
|
|
- parts.push(", ");
|
|
|
- return parts.slice(1).join("");
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- AssistEntry.prototype.loadEntries = function() {
|
|
|
- var self = this;
|
|
|
- if (!self.expandable || self.loading()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- self.loading(true);
|
|
|
- self.entries([]);
|
|
|
-
|
|
|
- // Defer this part to allow ko to react on empty entries and loading
|
|
|
- window.setTimeout(function() {
|
|
|
- self.assistSource.assistHelper.fetchPanelData(self.assistSource.snippet, self.getHierarchy(), function(data) {
|
|
|
- if (typeof data.tables !== "undefined") {
|
|
|
- self.entries($.map(data.tables, function(tableName) {
|
|
|
- return self.createEntry({
|
|
|
- name: tableName,
|
|
|
- displayName: tableName,
|
|
|
- title: tableName,
|
|
|
- isTable: true
|
|
|
- });
|
|
|
- }));
|
|
|
- } else if (typeof data.extended_columns !== "undefined" && data.extended_columns !== null) {
|
|
|
- self.entries($.map(data.extended_columns, function (columnDef) {
|
|
|
- var displayName = columnDef.name;
|
|
|
- if (typeof columnDef.type !== "undefined" && columnDef.type !== null) {
|
|
|
- displayName += ' (' + columnDef.type + ')'
|
|
|
- }
|
|
|
- var title = displayName;
|
|
|
- if (typeof columnDef.comment !== "undefined" && columnDef.comment !== null) {
|
|
|
- title += ' ' + columnDef.comment;
|
|
|
- }
|
|
|
- var shortType = null;
|
|
|
- if (typeof columnDef.type !== "undefined" && columnDef.type !== null) {
|
|
|
- shortType = columnDef.type.match(/^[^<]*/g)[0]; // everything before '<'
|
|
|
- }
|
|
|
- return self.createEntry({
|
|
|
- name: columnDef.name,
|
|
|
- displayName: displayName,
|
|
|
- title: title,
|
|
|
- isColumn: true,
|
|
|
- type: shortType
|
|
|
- });
|
|
|
- }));
|
|
|
- } else if (typeof data.columns !== "undefined" && data.columns !== null) {
|
|
|
- self.entries($.map(data.columns, function(columnName) {
|
|
|
- return self.createEntry({
|
|
|
- name: columnName,
|
|
|
- displayName: columnName,
|
|
|
- title: columnName,
|
|
|
- isColumn: true
|
|
|
- });
|
|
|
- }));
|
|
|
- } else if (typeof data.type !== "undefined" && data.type !== null) {
|
|
|
- if (data.type === "map") {
|
|
|
- self.entries([
|
|
|
- self.createEntry({
|
|
|
- name: "key",
|
|
|
- displayName: "key (" + data.key.type + ")",
|
|
|
- title: "key (" + data.key.type + ")",
|
|
|
- type: data.key.type
|
|
|
- }),
|
|
|
- self.createEntry({
|
|
|
- name: "value",
|
|
|
- displayName: "value (" + data.value.type + ")",
|
|
|
- title: "value (" + data.value.type + ")",
|
|
|
- isMapValue: true,
|
|
|
- type: data.value.type
|
|
|
- })
|
|
|
- ]);
|
|
|
- self.entries()[1].open(true);
|
|
|
- } else if (data.type == "struct") {
|
|
|
- self.entries($.map(data.fields, function(field) {
|
|
|
- return self.createEntry({
|
|
|
- name: field.name,
|
|
|
- displayName: field.name + " (" + field.type + ")",
|
|
|
- title: field.name + " (" + field.type + ")",
|
|
|
- type: field.type
|
|
|
- });
|
|
|
- }));
|
|
|
- } else if (data.type == "array") {
|
|
|
- self.entries([
|
|
|
- self.createEntry({
|
|
|
- name: "item",
|
|
|
- displayName: "item (" + data.item.type + ")",
|
|
|
- title: "item (" + data.item.type + ")",
|
|
|
- isArray: true,
|
|
|
- type: data.item.type
|
|
|
- })
|
|
|
- ]);
|
|
|
- self.entries()[0].open(true);
|
|
|
- }
|
|
|
- }
|
|
|
- self.loading(false);
|
|
|
- }, function() {
|
|
|
- self.assistSource.hasErrors(true);
|
|
|
- self.loading(false);
|
|
|
- });
|
|
|
- }, 10);
|
|
|
- };
|
|
|
-
|
|
|
- AssistEntry.prototype.createEntry = function(definition) {
|
|
|
- var self = this;
|
|
|
- return new AssistEntry(definition, self, self.assistSource, null)
|
|
|
- };
|
|
|
-
|
|
|
- AssistEntry.prototype.getHierarchy = function () {
|
|
|
- var self = this;
|
|
|
- var parts = [];
|
|
|
- var entry = self;
|
|
|
- while (entry != null) {
|
|
|
- parts.push(entry.definition.name);
|
|
|
- entry = entry.parent;
|
|
|
- }
|
|
|
- parts.reverse();
|
|
|
- return parts;
|
|
|
- };
|
|
|
-
|
|
|
- AssistEntry.prototype.dblClick = function (data, event) {
|
|
|
- var self = this;
|
|
|
- huePubSub.publish('assist.dblClickItem', self);
|
|
|
- };
|
|
|
-
|
|
|
- AssistEntry.prototype.toggleOpen = function () {
|
|
|
- var self = this;
|
|
|
- self.open(!self.open());
|
|
|
- };
|
|
|
-
|
|
|
- AssistEntry.prototype.showPreview = function () {
|
|
|
- var self = this;
|
|
|
- var $assistQuickLook = $("#assistQuickLook");
|
|
|
-
|
|
|
- var hierarchy = self.getHierarchy();
|
|
|
- var databaseName = hierarchy[0];
|
|
|
- var tableName = hierarchy[1];
|
|
|
-
|
|
|
- $assistQuickLook.find(".tableName").text(self.definition.name);
|
|
|
- $assistQuickLook.find(".tableLink").attr("href", "/metastore/table/" + self.assistSource.assistHelper.activeDatabase() + "/" + tableName);
|
|
|
- $assistQuickLook.find(".sample").empty("");
|
|
|
- $assistQuickLook.attr("style", "width: " + ($(window).width() - 120) + "px;margin-left:-" + (($(window).width() - 80) / 2) + "px!important;");
|
|
|
-
|
|
|
- self.assistSource.assistHelper.fetchTableHtmlPreview(self.assistSource.snippet, tableName, function(data) {
|
|
|
- $assistQuickLook.find(".loader").hide();
|
|
|
- $assistQuickLook.find(".sample").html(data);
|
|
|
- }, function(e) {
|
|
|
- if (e.status == 500) {
|
|
|
- $(document).trigger("error", "${ _('There was a problem loading the table preview.') }");
|
|
|
- $("#assistQuickLook").modal("hide");
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- $assistQuickLook.modal("show");
|
|
|
- };
|
|
|
-
|
|
|
- AssistEntry.prototype.showStats = function (data, event) {
|
|
|
- var self = this;
|
|
|
-
|
|
|
- if (self.statsVisible()) {
|
|
|
- self.statsVisible(false);
|
|
|
- self.assistSource.analysisStats(null);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- var hierarchy = self.getHierarchy();
|
|
|
- var databaseName = hierarchy[0];
|
|
|
- var tableName = hierarchy[1];
|
|
|
- var columnName = hierarchy.length == 3 ? hierarchy[2] : null;
|
|
|
-
|
|
|
- self.statsVisible(true);
|
|
|
- self.assistSource.analysisStats(new TableStats(self.assistSource, databaseName, tableName, columnName, self.definition.type));
|
|
|
-
|
|
|
- var catchChange = self.assistSource.analysisStats.subscribe(function(newValue) {
|
|
|
- if (newValue === null || newValue.database !== databaseName || newValue.table !== tableName || newValue.column !== columnName) {
|
|
|
- self.statsVisible(false);
|
|
|
- catchChange.dispose();
|
|
|
- }
|
|
|
- });
|
|
|
- $("#tableAnalysis").data("targetElement", $(event.target));
|
|
|
- };
|
|
|
-
|
|
|
- function TableStats (assistSource, database, table, column, type) {
|
|
|
- var self = this;
|
|
|
-
|
|
|
- self.snippet = assistSource.snippet;
|
|
|
- self.database = database;
|
|
|
- self.table = table;
|
|
|
- self.column = column;
|
|
|
- self.assistHelper = assistSource.assistHelper;
|
|
|
-
|
|
|
- self.loading = ko.observable(false);
|
|
|
- self.hasError = ko.observable(false);
|
|
|
- self.refreshing = ko.observable(false);
|
|
|
- self.loadingTerms = ko.observable(false);
|
|
|
- self.inaccurate = ko.observable(false);
|
|
|
- self.statRows = ko.observableArray();
|
|
|
- self.terms = ko.observableArray();
|
|
|
- self.termsTabActive = ko.observable(false);
|
|
|
- self.prefixFilter = ko.observable().extend({'throttle': 500});
|
|
|
- self.type = type;
|
|
|
- self.isComplexType = /^(map|array|struct)/i.test(type);
|
|
|
-
|
|
|
- self.prefixFilter.subscribe(function (newValue) {
|
|
|
- self.fetchTerms();
|
|
|
- });
|
|
|
-
|
|
|
- self.termsTabActive.subscribe(function (newValue) {
|
|
|
- if (self.terms().length == 0 && newValue) {
|
|
|
- self.fetchTerms();
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.fetchData();
|
|
|
- }
|
|
|
-
|
|
|
- TableStats.prototype.fetchData = function () {
|
|
|
- var self = this;
|
|
|
- self.loading(true);
|
|
|
- self.hasError(false);
|
|
|
- self.assistHelper.fetchStats(self.snippet, self.table, self.column != null ? self.column : null, function (data) {
|
|
|
- if (data && data.status == 0) {
|
|
|
- self.statRows(data.stats);
|
|
|
- var inaccurate = true;
|
|
|
- for(var i = 0; i < data.stats.length; i++) {
|
|
|
- if (data.stats[i].data_type == "COLUMN_STATS_ACCURATE" && data.stats[i].comment == "true") {
|
|
|
- inaccurate = false;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- self.inaccurate(inaccurate);
|
|
|
- } else if (data && data.message) {
|
|
|
- $(document).trigger("error", data.message);
|
|
|
- self.hasError(true);
|
|
|
- } else {
|
|
|
- $(document).trigger("error", "${ _('There was a problem loading the stats.') }");
|
|
|
- self.hasError(true);
|
|
|
- }
|
|
|
- self.loading(false);
|
|
|
- },
|
|
|
- function (e) {
|
|
|
- if (e.status == 500) {
|
|
|
- $(document).trigger("error", "${ _('There was a problem loading the stats.') }");
|
|
|
- }
|
|
|
- self.hasError(true);
|
|
|
- self.loading(false);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- TableStats.prototype.refresh = function () {
|
|
|
- var self = this;
|
|
|
- if (self.refreshing()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- var shouldFetchTerms = self.termsTabActive() || self.terms().length > 0;
|
|
|
- self.refreshing(true);
|
|
|
-
|
|
|
- self.assistHelper.refreshTableStats(self.snippet, self.table, self.column, function() {
|
|
|
- self.refreshing(false);
|
|
|
- self.fetchData();
|
|
|
- if (shouldFetchTerms) {
|
|
|
- self.fetchTerms();
|
|
|
- }
|
|
|
- }, function(message) {
|
|
|
- self.refreshing(false);
|
|
|
- $(document).trigger("error", message || "${ _('There was a problem refreshing the stats.') }");
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- TableStats.prototype.fetchTerms = function () {
|
|
|
- var self = this;
|
|
|
- if (self.column == null || (self.isComplexType && self.snippet.type() == "impala")) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- self.loadingTerms(true);
|
|
|
- self.assistHelper.fetchTerms(self.snippet, self.table, self.column, self.prefixFilter(), function (data) {
|
|
|
- if (data && data.status == 0) {
|
|
|
- self.terms($.map(data.terms, function (term) {
|
|
|
- return {
|
|
|
- name: term[0],
|
|
|
- count: term[1],
|
|
|
- percent: (parseFloat(term[1]) / parseFloat(data.terms[0][1])) * 100
|
|
|
- }
|
|
|
- }));
|
|
|
- } else if (data && data.message) {
|
|
|
- $(document).trigger("error", data.message);
|
|
|
- } else {
|
|
|
- $(document).trigger("error", "${ _('There was a problem loading the terms.') }");
|
|
|
- }
|
|
|
- self.loadingTerms(false);
|
|
|
- }, function (e) {
|
|
|
- if (e.status == 500) {
|
|
|
- $(document).trigger("error", "${ _('There was a problem loading the terms.') }");
|
|
|
- }
|
|
|
- self.loadingTerms(false);
|
|
|
- });
|
|
|
- };
|
|
|
+ }(function (ko, AssistSource) {
|
|
|
|
|
|
function AssistPanel(params) {
|
|
|
var notebookViewModel = params.notebookViewModel;
|
|
|
@@ -693,137 +331,8 @@ from desktop.views import _ko
|
|
|
self.selectedSourceType.subscribe(function (newSourceType) {
|
|
|
$.totalStorage("hue.assist.lastSelectedSource." + notebookViewModel.user, newSourceType);
|
|
|
});
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- function AssistSource(snippet) {
|
|
|
- var self = this;
|
|
|
- self.snippet = snippet;
|
|
|
- self.assistHelper = snippet.getAssistHelper();
|
|
|
-
|
|
|
- self.hasErrors = ko.observable(false);
|
|
|
- self.simpleStyles = ko.observable(false);
|
|
|
-
|
|
|
- self.filter = ko.observable("").extend({ rateLimit: 150 });
|
|
|
-
|
|
|
- self.filterActive = ko.computed(function () {
|
|
|
- return self.filter().length !== 0;
|
|
|
- });
|
|
|
-
|
|
|
- self.options = ko.mapping.fromJS($.extend({
|
|
|
- isSearchVisible: false
|
|
|
- }, $.totalStorage(snippet.type() + ".assist.options") || {}));
|
|
|
-
|
|
|
- $.each(Object.keys(self.options), function (index, key) {
|
|
|
- if (ko.isObservable(self.options[key])) {
|
|
|
- self.options[key].subscribe(function() {
|
|
|
- $.totalStorage(snippet.type() + ".assist.options", ko.mapping.toJS(self.options))
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.databases = ko.observableArray();
|
|
|
- self.selectedDatabase = ko.observable();
|
|
|
-
|
|
|
- self.reloading = ko.observable(false);
|
|
|
-
|
|
|
- self.loadingTables = ko.computed(function() {
|
|
|
- return typeof self.selectedDatabase() != "undefined" && self.selectedDatabase() !== null && self.selectedDatabase().loading();
|
|
|
- });
|
|
|
-
|
|
|
- self.selectedDatabase.subscribe(function (newValue) {
|
|
|
- if (newValue != null && !newValue.hasEntries() && !newValue.loading()) {
|
|
|
- newValue.loadEntries()
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- var updateDatabases = function (names) {
|
|
|
- self.databases($.map(names, function(name) {
|
|
|
- return new AssistEntry({
|
|
|
- name: name,
|
|
|
- displayName: name,
|
|
|
- title: name,
|
|
|
- isDatabase: true
|
|
|
- }, null, self, self.filter);
|
|
|
- }));
|
|
|
-
|
|
|
- self.setDatabase(self.assistHelper.activeDatabase());
|
|
|
- };
|
|
|
-
|
|
|
- self.assistHelper.activeDatabase.subscribe(function(newValue) {
|
|
|
- self.setDatabase(newValue);
|
|
|
- });
|
|
|
-
|
|
|
- updateDatabases(self.assistHelper.availableDatabases());
|
|
|
- self.assistHelper.loaded.subscribe(function (newValue) {
|
|
|
- if (newValue) {
|
|
|
- updateDatabases(self.assistHelper.availableDatabases());
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.modalItem = ko.observable();
|
|
|
- self.analysisStats = ko.observable();
|
|
|
-
|
|
|
- var lastOffset = { top: -1, left: -1 };
|
|
|
- self.refreshPosition = function () {
|
|
|
- if (self.analysisStats() == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- var $tableAnalysis = $("#tableAnalysis");
|
|
|
- var targetElement = $tableAnalysis.data("targetElement");
|
|
|
- if (targetElement != null && targetElement.is(":visible")) {
|
|
|
- var newTop = targetElement.offset().top - $(window).scrollTop();
|
|
|
- if (targetElement != null && (lastOffset.left != targetElement.offset().left || lastOffset.top != newTop)) {
|
|
|
- lastOffset.left = targetElement.offset().left;
|
|
|
- lastOffset.top = newTop;
|
|
|
- var newCssTop = lastOffset.top - $tableAnalysis.outerHeight() / 2 + targetElement.outerHeight() / 2;
|
|
|
- $tableAnalysis.css("top", newCssTop).css("left", lastOffset.left + targetElement.outerWidth());
|
|
|
- if ((newCssTop + $tableAnalysis.outerHeight() / 2) < 70) {
|
|
|
- $tableAnalysis.hide();
|
|
|
- } else {
|
|
|
- $tableAnalysis.show();
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- $tableAnalysis.hide();
|
|
|
- }
|
|
|
- };
|
|
|
- window.setInterval(self.refreshPosition, 200);
|
|
|
-
|
|
|
- self.repositionActions = function(data, event) {
|
|
|
- if (data.definition.isDatabase) {
|
|
|
- var $container = $(event.target);
|
|
|
- $container.find(".assist-actions").css('right', -$container.scrollLeft() + 'px');
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- AssistSource.prototype.setDatabase = function(name) {
|
|
|
- var self = this;
|
|
|
- if (name == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- self.selectedDatabase(ko.utils.arrayFirst(self.databases(), function(database) {
|
|
|
- return name === database.definition.name;
|
|
|
- }));
|
|
|
- };
|
|
|
-
|
|
|
- AssistSource.prototype.toggleSearch = function () {
|
|
|
- var self = this;
|
|
|
- self.options.isSearchVisible(!self.options.isSearchVisible());
|
|
|
- };
|
|
|
-
|
|
|
- AssistSource.prototype.reloadAssist = function() {
|
|
|
- var self = this;
|
|
|
- self.reloading(true);
|
|
|
- self.selectedDatabase(null);
|
|
|
- self.assistHelper.clearCache(self.snippet);
|
|
|
- self.assistHelper.load(self.snippet, function() {
|
|
|
- self.reloading(false);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
ko.components.register('assist-panel', {
|
|
|
viewModel: AssistPanel,
|
|
|
template: { element: 'assist-panel-template' }
|