Переглянути джерело

[assist] Extract assist modules into desktop/js/assist

Johan Ahlen 10 роки тому
батько
коміт
e7caed6

+ 4 - 1
apps/beeswax/src/beeswax/templates/execute.mako

@@ -808,7 +808,10 @@ ${ commonshare() | n,unicode }
 <script src="${ static('desktop/js/ko.hue-bindings.js') }" type="text/javascript" charset="utf-8"></script>
 <script src="${ static('desktop/js/sqlAutocompleter.js') }" type="text/javascript" charset="utf-8"></script>
 <script src="${ static('desktop/js/hdfsAutocompleter.js') }" type="text/javascript" charset="utf-8"></script>
-<script src="${ static('desktop/js/assistHelper.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/js/assist/tableStats.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/js/assist/assistHelper.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/js/assist/assistEntry.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/js/assist/assistSource.js') }" type="text/javascript" charset="utf-8"></script>
 <script src="${ static('desktop/js/autocompleter.js') }" type="text/javascript" charset="utf-8"></script>
 <script src="${ static('notebook/js/notebook.ko.js') }" type="text/javascript" charset="utf-8"></script>
 <script src="${ static('beeswax/js/beeswax.vm.js') }"></script>

+ 272 - 0
desktop/core/src/desktop/static/desktop/js/assist/assistEntry.js

@@ -0,0 +1,272 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function (root, factory) {
+  if(typeof define === "function" && define.amd) {
+    define(['knockout', 'desktop/js/assist/tableStats'], factory);
+  } else {
+    root.AssistEntry = factory(ko, TableStats);
+  }
+}(this, function (ko, TableStats) {
+
+  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));
+  };
+
+  return AssistEntry;
+}));

+ 0 - 0
desktop/core/src/desktop/static/desktop/js/assistHelper.js → desktop/core/src/desktop/static/desktop/js/assist/assistHelper.js


+ 154 - 0
desktop/core/src/desktop/static/desktop/js/assist/assistSource.js

@@ -0,0 +1,154 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function (root, factory) {
+  if(typeof define === "function" && define.amd) {
+    define(['knockout', 'desktop/js/assist/assistEntry'], factory);
+  } else {
+    root.AssistHelper = factory(ko, AssistEntry);
+  }
+}(this, function (ko, AssistEntry) {
+
+  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);
+    });
+  };
+
+  return AssistSource;
+}));

+ 143 - 0
desktop/core/src/desktop/static/desktop/js/assist/tableStats.js

@@ -0,0 +1,143 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function (root, factory) {
+  if(typeof define === "function" && define.amd) {
+    define(['knockout'], factory);
+  } else {
+    root.TableStats = factory(ko);
+  }
+}(this, function (ko) {
+
+  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);
+    });
+  };
+
+  return TableStats;
+}));

+ 1 - 1
desktop/core/src/desktop/static/desktop/spec/sqlAutocompleterSpec.js

@@ -16,7 +16,7 @@
 define([
   'knockout',
   'desktop/js/sqlAutocompleter',
-  'desktop/js/assistHelper',
+  'desktop/js/assist/assistHelper',
   'desktop/spec/autocompleterTestUtils'
 ], function(ko, SqlAutocompleter, AssistHelper, testUtils) {
   describe("sqlAutocompleter.js", function() {

+ 3 - 494
desktop/core/src/desktop/templates/ko_components.mako

@@ -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' }

+ 1 - 1
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -16,7 +16,7 @@
 
 (function (root, factory) {
   if(typeof define === "function" && define.amd) {
-    define(['knockout', 'desktop/js/assistHelper', 'desktop/js/autocompleter', 'knockout-mapping', 'ko.charts'], factory);
+    define(['knockout', 'desktop/js/assist/assistHelper', 'desktop/js/autocompleter', 'knockout-mapping', 'ko.charts'], factory);
   } else {
     root.EditorViewModel = factory(ko, AssistHelper, Autocompleter);
   }