浏览代码

[assist] Extract table stats to separate component

Johan Ahlen 10 年之前
父节点
当前提交
0014b99

+ 2 - 0
apps/beeswax/src/beeswax/templates/execute.mako

@@ -23,6 +23,7 @@
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="dashboard" file="common_dashboard.mako" />
+<%namespace name="tableStats" file="/table_stats.mako" />
 <%namespace name="assist" file="/assist.mako" />
 
 ${ commonheader(_('Query'), app_name, user) | n,unicode }
@@ -838,6 +839,7 @@ ${ commonshare() | n,unicode }
 
 
 ${ assist.assistPanel() }
+${ tableStats.tableStats() }
 
 <style type="text/css">
   h1 {

+ 14 - 29
desktop/core/src/desktop/static/desktop/js/assist/assistEntry.js

@@ -16,11 +16,11 @@
 
 (function (root, factory) {
   if(typeof define === "function" && define.amd) {
-    define(['knockout', 'desktop/js/assist/tableStats'], factory);
+    define(['knockout'], factory);
   } else {
-    root.AssistEntry = factory(ko, TableStats);
+    root.AssistEntry = factory(ko);
   }
-}(this, function (ko, TableStats) {
+}(this, function (ko) {
 
   function AssistEntry (definition, parent, assistSource, filter, i18n) {
     var self = this;
@@ -62,6 +62,17 @@
       return result;
     });
 
+    self.tableName = null;
+    self.columnName = null;
+    self.databaseName = self.getHierarchy()[0];
+    if (self.definition.isTable) {
+      self.tableName = self.definition.name;
+      self.columnName = null;
+    } else if (self.definition.isColumn) {
+      self.tableName = parent.definition.name;
+      self.columnName = self.definition.name;
+    }
+
     self.editorText = ko.computed(function () {
       if (self.definition.isTable) {
         return self.definition.name;
@@ -243,31 +254,5 @@
     $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 - 27
desktop/core/src/desktop/static/desktop/js/assist/assistSource.js

@@ -113,33 +113,6 @@
     });
 
     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) {

+ 9 - 9
desktop/core/src/desktop/static/desktop/js/assist/tableStats.js

@@ -22,14 +22,16 @@
   }
 }(this, function (ko) {
 
-  function TableStats (assistSource, database, table, column, type, i18n) {
+  function TableStats (options) {
     var self = this;
-    self.i18n = i18n;
-    self.snippet = assistSource.snippet;
-    self.database = database;
-    self.table = table;
-    self.column = column;
-    self.assistHelper = assistSource.assistHelper;
+    self.i18n = options.i18n;
+    self.snippet = options.snippet;
+    self.database = options.databaseName;
+    self.table = options.tableName;
+    self.column = options.columnName;
+    self.assistHelper = options.assistHelper;
+    self.type = options.type;
+    self.isComplexType = /^(map|array|struct)/i.test(options.type);
 
     self.loading = ko.observable(false);
     self.hasError = ko.observable(false);
@@ -40,8 +42,6 @@
     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();

+ 10 - 74
desktop/core/src/desktop/templates/assist.mako

@@ -116,51 +116,6 @@ from desktop.views import _ko
     }
   </style>
 
-  <script type="text/html" id="assist-panel-table-stats">
-    <div class="content">
-      <!-- ko if: statRows().length -->
-      <table class="table table-striped">
-        <tbody data-bind="foreach: statRows">
-          <tr><th data-bind="text: data_type"></th><td data-bind="text: comment"></td></tr>
-        </tbody>
-      </table>
-      <!-- /ko -->
-    </div>
-  </script>
-
-  <script type="text/html" id="assist-panel-column-stats">
-    <div class="pull-right filter" data-bind="visible: termsTabActive" style="display:none;">
-      <input type="text" data-bind="textInput: prefixFilter" placeholder="${ _('Prefix filter...') }"/>
-    </div>
-    <ul class="nav nav-tabs" role="tablist" style="margin-bottom: 0">
-      <li data-bind="click: function() { termsTabActive(false) }" class="active"><a href="#columnAnalysisStats" role="tab" data-toggle="tab">${ _('Stats') }</a></li>
-      <li data-bind="click: function() { termsTabActive(true) }"><a href="#columnAnalysisTerms" role="tab" data-toggle="tab">${ _('Terms') }</a></li>
-    </ul>
-    <div class="tab-content">
-      <div class="tab-pane active" id="columnAnalysisStats" style="text-align: left">
-        <div class="alert" data-bind="visible: isComplexType" style="margin: 5px">${ _('Column stats are currently not supported for columns of type:') } <span data-bind="text: type"></span></div>
-        <div class="content" data-bind="ifnot: isComplexType">
-          <table class="table table-striped">
-            <tbody data-bind="foreach: statRows">
-              <tr><th data-bind="text: Object.keys($data)[0]"></th><td data-bind="text: $data[Object.keys($data)[0]]"></td></tr>
-            </tbody>
-          </table>
-        </div>
-      </div>
-      <div class="tab-pane" id="columnAnalysisTerms" style="text-align: left">
-        <i style="margin: 5px;" data-bind="visible: loadingTerms" class='fa fa-spinner fa-spin'></i>
-        <div class="alert" data-bind="visible: ! loadingTerms() && terms().length == 0">${ _('There are no terms to be shown') }</div>
-        <div class="content">
-          <table class="table table-striped" data-bind="visible: ! loadingTerms()">
-            <tbody data-bind="foreach: terms">
-              <tr><td data-bind="text: name"></td><td style="width: 40px"><div class="progress"><div class="bar-label" data-bind="text: count"></div><div class="bar bar-info" style="margin-top: -20px;" data-bind="style: { 'width' : percent + '%' }"></div></div></td></tr>
-            </tbody>
-          </table>
-        </div>
-      </div>
-    </div>
-  </script>
-
   <script type="text/html" id="assist-no-entries">
     <ul class="assist-tables">
       <li data-bind="visible: definition.isDatabase">
@@ -175,7 +130,15 @@ from desktop.views import _ko
   <script type="text/html" id="assist-entry-actions">
     <div class="assist-actions" data-bind="css: { 'table-actions' : definition.isTable, 'column-actions': definition.isColumn } " style="opacity: 0">
       <a class="inactive-action" href="javascript:void(0)" data-bind="visible: definition.isTable, click: showPreview"><i class="fa fa-list" title="${_('Preview Sample data')}"></i></a>
-      <a class="inactive-action" href="javascript:void(0)" data-bind="visible: definition.isTable || definition.isColumn, click: showStats, css: { 'blue': statsVisible }"><i class='fa fa-bar-chart' title="${_('View statistics') }"></i></a>
+      <span data-bind="component: { name: 'table-stats', params: {
+          statsVisible: statsVisible,
+          snippet: assistSource.snippet,
+          databaseName: databaseName,
+          tableName: tableName,
+          columnName: columnName,
+          fieldType: definition.type,
+          assistHelper: assistSource.assistHelper
+        } }"></span>
     </div>
   </script>
 
@@ -294,30 +257,6 @@ from desktop.views import _ko
         <button class="btn btn-primary disable-feedback" data-dismiss="modal">${_('Ok')}</button>
       </div>
     </div>
-
-    <div id="tableAnalysis" style="position: fixed; display: none;" class="popover show mega-popover right" data-bind="visible: $parent.analysisStats() != null, with: $parent.analysisStats">
-      <div class="arrow"></div>
-      <h3 class="popover-title" style="text-align: left">
-        <a class="pull-right pointer close-popover" style="margin-left: 8px" data-bind="click: function() { $parents[1].analysisStats(null) }"><i class="fa fa-times"></i></a>
-        <a class="pull-right pointer stats-refresh" style="margin-left: 8px" data-bind="visible: !isComplexType, click: refresh"><i class="fa fa-refresh" data-bind="css: { 'fa-spin' : refreshing }"></i></a>
-        <span class="pull-right stats-warning muted" data-bind="visible: inaccurate() && column == null" rel="tooltip" data-placement="top" title="${ _('The column stats for this table are not accurate') }" style="margin-left: 8px"><i class="fa fa-exclamation-triangle"></i></span>
-        <i data-bind="visible: loading" class='fa fa-spinner fa-spin'></i>
-        <!-- ko if: column == null -->
-        <strong class="table-name" data-bind="text: table"></strong> ${ _(' table analysis') }
-        <!-- /ko -->
-        <!-- ko ifnot: column == null -->
-        <strong class="table-name" data-bind="text: column"></strong> ${ _(' column analysis') }
-        <!-- /ko -->
-      </h3>
-      <div class="popover-content">
-        <div class="alert" style="text-align: left; display:none" data-bind="visible: hasError">${ _('There is no analysis available') }</div>
-        <!-- ko if: isComplexType && snippet.type() == 'impala' -->
-        <div class="alert" style="text-align: left">${ _('Column analysis is currently not supported for columns of type:') } <span data-bind="text: type"></span></div>
-        <!-- /ko -->
-        <!-- ko template: {if: column == null && ! hasError() && ! (isComplexType && snippet.type() == 'impala'), name: 'assist-panel-table-stats' } --><!-- /ko -->
-        <!-- ko template: {if: column != null && ! hasError() && ! (isComplexType && snippet.type() == 'impala'), name: 'assist-panel-column-stats' } --><!-- /ko -->
-      </div>
-    </div>
   </script>
 
   <script type="text/javascript" charset="utf-8">
@@ -332,10 +271,7 @@ from desktop.views import _ko
       function AssistPanel(params) {
         var self = this;
         var i18n = {
-          errorLoadingTablePreview: "${ _('There was a problem loading the table preview.') }",
-          errorLoadingStats: "${ _('There was a problem loading the stats.') }",
-          errorRefreshingStats: "${ _('There was a problem refreshing the stats.') }",
-          errorLoadingTerms: "${ _('There was a problem loading the terms.') }"
+          errorLoadingTablePreview: "${ _('There was a problem loading the table preview.') }"
         };
         var notebookViewModel = params.notebookViewModel;
         var notebook = notebookViewModel.selectedNotebook();

+ 195 - 0
desktop/core/src/desktop/templates/table_stats.mako

@@ -0,0 +1,195 @@
+## 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.
+
+<%!
+from desktop import conf
+from desktop.lib.i18n import smart_unicode
+from django.utils.translation import ugettext as _
+from desktop.views import _ko
+%>
+
+<%def name="tableStats()">
+  <script type="text/html" id="table-stats">
+    <div class="content">
+      <!-- ko if: statRows().length -->
+      <table class="table table-striped">
+        <tbody data-bind="foreach: statRows">
+          <tr><th data-bind="text: data_type"></th><td data-bind="text: comment"></td></tr>
+        </tbody>
+      </table>
+      <!-- /ko -->
+    </div>
+  </script>
+
+  <script type="text/html" id="column-stats">
+    <div class="pull-right filter" data-bind="visible: termsTabActive" style="display:none;">
+      <input type="text" data-bind="textInput: prefixFilter" placeholder="${ _('Prefix filter...') }"/>
+    </div>
+    <ul class="nav nav-tabs" role="tablist" style="margin-bottom: 0">
+      <li data-bind="click: function() { termsTabActive(false) }" class="active"><a href="#columnAnalysisStats" role="tab" data-toggle="tab">${ _('Stats') }</a></li>
+      <li data-bind="click: function() { termsTabActive(true) }"><a href="#columnAnalysisTerms" role="tab" data-toggle="tab">${ _('Terms') }</a></li>
+    </ul>
+    <div class="tab-content">
+      <div class="tab-pane active" id="columnAnalysisStats" style="text-align: left">
+        <div class="alert" data-bind="visible: isComplexType" style="margin: 5px">${ _('Column stats are currently not supported for columns of type:') } <span data-bind="text: type"></span></div>
+        <div class="content" data-bind="ifnot: isComplexType">
+          <table class="table table-striped">
+            <tbody data-bind="foreach: statRows">
+              <tr><th data-bind="text: Object.keys($data)[0]"></th><td data-bind="text: $data[Object.keys($data)[0]]"></td></tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+      <div class="tab-pane" id="columnAnalysisTerms" style="text-align: left">
+        <i style="margin: 5px;" data-bind="visible: loadingTerms" class='fa fa-spinner fa-spin'></i>
+        <div class="alert" data-bind="visible: ! loadingTerms() && terms().length == 0">${ _('There are no terms to be shown') }</div>
+        <div class="content">
+          <table class="table table-striped" data-bind="visible: ! loadingTerms()">
+            <tbody data-bind="foreach: terms">
+              <tr><td data-bind="text: name"></td><td style="width: 40px"><div class="progress"><div class="bar-label" data-bind="text: count"></div><div class="bar bar-info" style="margin-top: -20px;" data-bind="style: { 'width' : percent + '%' }"></div></div></td></tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+  </script>
+
+  <script type="text/html" id="stats-popover">
+    <div style="position: fixed; display: none;" class="popover show mega-popover right" data-bind="style: { 'top': popoverTop() + 'px', 'left': popoverLeft() + 'px' }, visible: analysisStats, with: analysisStats">
+      <div class="arrow"></div>
+      <h3 class="popover-title" style="text-align: left">
+        <a class="pull-right pointer close-popover" style="margin-left: 8px" data-bind="click: $parent.toggleStats"><i class="fa fa-times"></i></a>
+        <a class="pull-right pointer stats-refresh" style="margin-left: 8px" data-bind="visible: !isComplexType, click: refresh"><i class="fa fa-refresh" data-bind="css: { 'fa-spin' : refreshing }"></i></a>
+        <span class="pull-right stats-warning muted" data-bind="visible: inaccurate() && column == null" rel="tooltip" data-placement="top" title="${ _('The column stats for this table are not accurate') }" style="margin-left: 8px"><i class="fa fa-exclamation-triangle"></i></span>
+        <i data-bind="visible: loading" class='fa fa-spinner fa-spin'></i>
+        <!-- ko if: column == null -->
+        <strong class="table-name" data-bind="text: table"></strong> ${ _(' table analysis') }
+        <!-- /ko -->
+        <!-- ko ifnot: column == null -->
+        <strong class="table-name" data-bind="text: column"></strong> ${ _(' column analysis') }
+        <!-- /ko -->
+      </h3>
+      <div class="popover-content">
+        <div class="alert" style="text-align: left; display:none" data-bind="visible: hasError">${ _('There is no analysis available') }</div>
+        <!-- ko if: isComplexType && snippet.type() == 'impala' -->
+        <div class="alert" style="text-align: left">${ _('Column analysis is currently not supported for columns of type:') } <span data-bind="text: type"></span></div>
+        <!-- /ko -->
+        <!-- ko template: {if: column == null && ! hasError() && ! (isComplexType && snippet.type() == 'impala'), name: 'table-stats' } --><!-- /ko -->
+        <!-- ko template: {if: column != null && ! hasError() && ! (isComplexType && snippet.type() == 'impala'), name: 'column-stats' } --><!-- /ko -->
+      </div>
+    </div>
+  </script>
+
+  <script type="text/html" id="table-stats-link">
+    <a class="inactive-action" href="javascript:void(0)" data-bind="visible: enabled, click: toggleStats, css: { 'blue': analysisStats }"><i class='fa fa-bar-chart' title="${_('View statistics') }"></i></a>
+    <!-- ko template: { if: analysisStats, name: 'stats-popover'} --><!-- /ko -->
+  </script>
+
+  <script type="text/javascript" charset="utf-8">
+    (function (factory) {
+      if(typeof require === "function") {
+        require(['knockout', 'desktop/js/assist/tableStats'], factory);
+      } else {
+        factory(ko, TableStats);
+      }
+    }(function (ko, TableStats) {
+
+      function TableStatsViewModel(params, element) {
+        var self = this;
+        self.params = params;
+        var $targetElement = $(element);
+        self.i18n = {
+          errorLoadingStats: "${ _('There was a problem loading the stats.') }",
+          errorRefreshingStats: "${ _('There was a problem refreshing the stats.') }",
+          errorLoadingTerms: "${ _('There was a problem loading the terms.') }"
+        };
+
+        self.enabled = params.tableName || params.columnName;
+        self.analysisStats = ko.observable(null);
+
+        if (ko.isObservable(self.params.statsVisible)) {
+          self.analysisStats.subscribe(function (newValue) {
+            self.params.statsVisible(newValue !== null);
+          })
+        }
+
+        self.popoverTop = ko.observable(0);
+        self.popoverLeft = ko.observable(0);
+
+        var lastOffset = { top: -1, left: -1 };
+        self.refreshPopoverPosition = function () {
+          var $popover = $targetElement.find(".popover");
+          if ($targetElement.is(":visible")) {
+            var newTop = $targetElement.offset().top - $(window).scrollTop();
+            if (lastOffset.left != $targetElement.offset().left || lastOffset.top != newTop) {
+              lastOffset.left = $targetElement.offset().left;
+              lastOffset.top = newTop;
+              self.popoverTop(lastOffset.top - ($popover.length ? ($popover.outerHeight()-16) / 2 : 200));
+              self.popoverLeft(lastOffset.left + $targetElement.outerWidth());
+              if ((self.popoverTop()) < -130) {
+                $popover.hide();
+              } else {
+                $popover.show();
+              }
+            }
+          } else {
+            $popover.hide();
+          }
+        }
+
+        if (self.enabled) {
+          window.setInterval(function () {
+            if (self.analysisStats() == null) {
+              return;
+            }
+
+            self.refreshPopoverPosition();
+          }, 200);
+        }
+
+        self.analysisStats.subscribe(function (newValue) {
+          if (newValue) {
+            self.refreshPopoverPosition();
+          }
+        });
+
+        self.toggleStats = function (data, event) {
+          if (self.analysisStats()) {
+            self.analysisStats(null);
+          } else {
+            self.analysisStats(new TableStats({
+              i18n: self.i18n,
+              snippet: self.params.snippet,
+              databaseName: self.params.databaseName,
+              tableName: self.params.tableName,
+              columnName: self.params.columnName,
+              assistHelper: self.params.assistHelper,
+              type: self.params.fieldType
+            }));
+          }
+        };
+      }
+      ko.components.register('table-stats', {
+        viewModel: {
+          createViewModel: function(params, componentInfo) {
+            return new TableStatsViewModel(params, componentInfo.element);
+          }
+        },
+        template: { element: 'table-stats-link' }
+      });
+    }));
+  </script>
+</%def>

+ 2 - 1
desktop/libs/notebook/src/notebook/templates/editor.mako

@@ -20,6 +20,7 @@
 %>
 
 <%namespace name="koComponents" file="/ko_components.mako" />
+<%namespace name="tableStats" file="/table_stats.mako" />
 <%namespace name="assist" file="/assist.mako" />
 <%namespace name="editorComponents" file="editor_components.mako" />
 
@@ -38,7 +39,7 @@ ${ editorComponents.includes() }
 ${ editorComponents.topBar('editor', editor_type) }
 ${ editorComponents.commonHTML() }
 
-
+${ tableStats.tableStats() }
 ${ assist.assistPanel() }
 ${ koComponents.csvListInput() }
 ${ koComponents.jvmMemoryInput() }

+ 2 - 0
desktop/libs/notebook/src/notebook/templates/notebook.mako

@@ -19,6 +19,7 @@
   from django.utils.translation import ugettext as _
 %>
 
+<%namespace name="tableStats" file="/table_stats.mako" />
 <%namespace name="assist" file="/assist.mako" />
 <%namespace name="koComponents" file="/ko_components.mako" />
 <%namespace name="editorComponents" file="editor_components.mako" />
@@ -54,6 +55,7 @@ ${ editorComponents.topBar('notebook') }
   </%def>
 </%editorComponents:commonHTML>
 
+${ tableStats.tableStats() }
 ${ assist.assistPanel() }
 ${ koComponents.csvListInput() }
 ${ koComponents.jvmMemoryInput() }