浏览代码

[notebook] Support multiple sources in the assist panel

The assist panel now has a drop down to select the source. It will call the the notebook API where possible and as a result the autocomplete now shares the same cache.
Johan Ahlen 10 年之前
父节点
当前提交
31734d2

+ 5 - 0
desktop/core/src/desktop/static/desktop/js/autocomplete.js

@@ -31,6 +31,11 @@ function Autocompleter(options) {
   self.assistHelper = options.assistHelper;
 }
 
+Autocompleter.prototype.setAssistHelper = function (assistHelper) {
+  var self = this;
+  self.assistHelper = assistHelper;
+};
+
 // ACE Format for autocompleter
 Autocompleter.prototype.getCompletions = function (editor, session, pos, prefix, callback) {
   var self = this;

+ 2 - 2
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -1648,7 +1648,7 @@ ko.bindingHandlers.aceEditor = {
     var $el = $(element);
     var options = ko.unwrap(valueAccessor());
     var snippet = options.snippet;
-    var assistHelper = options.assistHelper;
+    var assistHelper = snippet.getAssistHelper();
 
     $el.text(snippet.statement_raw());
 
@@ -1739,7 +1739,7 @@ ko.bindingHandlers.aceEditor = {
     var refreshTables = function() {
       currentAssistTables = {};
       if (typeof assistHelper.activeDatabase() !== "undefined" && assistHelper.activeDatabase() != null) {
-        assistHelper.fetchTables(function(data) {
+        assistHelper.fetchTables(snippet, function(data) {
           $.each(data.tables, function(index, table) {
             currentAssistTables[table] = true;
           });

+ 112 - 69
desktop/core/src/desktop/templates/ko_components.mako

@@ -154,7 +154,7 @@ from desktop.views import _ko
   </script>
 
   <script type="text/html" id="assist-entries">
-    <ul data-bind="foreach: filteredEntries, css: { 'assist-tables': definition.isDatabase }, event: { 'scroll': assistPanel.repositionActions }">
+    <ul data-bind="foreach: filteredEntries, css: { 'assist-tables': definition.isDatabase }, event: { 'scroll': assistSource.repositionActions }">
       <li data-bind="css: { 'assist-table reveals-actions-2nd': definition.isTable, 'assist-column reveals-actions-3rd': definition.isColumn }">
         <!-- ko template: { if: definition.isTable || definition.isColumn, name: 'assist-entry-actions' } --><!-- /ko -->
         <a class="assist-column-link" data-bind="multiClick: { click: toggleOpen, dblClick: dblClick }, attr: {'title': definition.title }, css: { 'assist-field-link': ! definition.isTable, 'assist-table-link': definition.isTable }" href="javascript:void(0)">
@@ -167,49 +167,62 @@ from desktop.views import _ko
   </script>
 
   <script type="text/html" id="assist-panel-template">
+    <ul class="nav nav-list" style="position:relative; border: none; padding: 0; background-color: #FFF; margin-bottom: 1px; width:100%;">
+      <li class="nav-header">
+        ${_('source')}
+      </li>
+      <li>
+        <select data-bind="options: availableSourceTypes, select2: { width: '100%', placeholder: '${ _ko("Choose a source...") }', update: selectedSourceType }" class="input-medium" data-placeholder="${_('Choose a source...')}"></select>
+      </li>
+
+      <!-- ko with: selectedSource -->
+      <!-- ko template: { name: "assist-type-template" } --><!-- /ko -->
+      <!-- /ko -->
+    </ul>
+  </script>
+
+  <script type="text/html" id="assist-type-template">
     <div class="reveals-actions" style="position: relative; width:100%">
-      <ul class="nav nav-list" style="position:relative; border: none; padding: 0; background-color: #FFF; margin-bottom: 1px; width:100%;">
-        <li class="nav-header">
-          ${_('database')}
-          <div class="pull-right" data-bind="css: { 'hover-actions' : ! reloading() }">
-            <a href="javascript:void(0)" data-bind="click: reloadAssist"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin' : reloading }" title="${_('Manually refresh the table list')}"></i></a>
-          </div>
-        </li>
+      <li class="nav-header">
+        ${_('database')}
+        <div class="pull-right" data-bind="css: { 'hover-actions' : ! reloading() }">
+          <a href="javascript:void(0)" data-bind="click: reloadAssist"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin' : reloading }" title="${_('Manually refresh the table list')}"></i></a>
+        </div>
+      </li>
 
-        <li data-bind="visible: ! hasErrors() && ! loadingDatabases()" >
-          <select data-bind="options: availableDatabaseNames, select2: { width: '100%', placeholder: '${ _ko("Choose a database...") }', update: assistHelper.activeDatabase }" class="input-medium" data-placeholder="${_('Choose a database...')}"></select>
-        </li>
+      <li data-bind="visible: ! hasErrors() && ! loadingDatabases()" >
+        <select data-bind="options: availableDatabaseNames, select2: { width: '100%', placeholder: '${ _ko("Choose a database...") }', update: assistHelper.activeDatabase }" class="input-medium" data-placeholder="${_('Choose a database...')}"></select>
+      </li>
 
-        <li class="center" data-bind="visible: loadingDatabases" >
-          <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i><!--<![endif]-->
-          <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }"/><![endif]-->
-        </li>
+      <li class="center" data-bind="visible: loadingDatabases" >
+        <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i><!--<![endif]-->
+        <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }"/><![endif]-->
+      </li>
 
-        <li data-bind="visible: hasErrors">
-          <span>${ _('The database list cannot be loaded.') }</span>
-        </li>
+      <li data-bind="visible: hasErrors">
+        <span>${ _('The database list cannot be loaded.') }</span>
+      </li>
 
-        <li class="nav-header" style="margin-top:10px;" data-bind="visible: ! loadingDatabases() && ! hasErrors()">
-          ${_('tables')}
-          <div class="pull-right" data-bind="visible: selectedDatabase() != null && selectedDatabase().hasEntries(), css: { 'hover-actions': ! filter(), 'blue': filter }">
-            <a href="javascript:void(0)" data-bind="click: toggleSearch"><i class="pointer fa fa-search" title="${_('Search')}"></i></a>
-          </div>
-        </li>
+      <li class="nav-header" style="margin-top:10px;" data-bind="visible: ! loadingDatabases() && ! hasErrors()">
+        ${_('tables')}
+        <div class="pull-right" data-bind="visible: selectedDatabase() != null && selectedDatabase().hasEntries(), css: { 'hover-actions': ! filter(), 'blue': filter }">
+          <a href="javascript:void(0)" data-bind="click: toggleSearch"><i class="pointer fa fa-search" title="${_('Search')}"></i></a>
+        </div>
+      </li>
 
-        <!-- ko if: selectedDatabase() != null -->
-          <li data-bind="slideVisible: selectedDatabase() != null && selectedDatabase().hasEntries() && options.isSearchVisible()">
-            <div><input type="text" placeholder="${ _('Table name...') }" style="width:90%;" data-bind="value: filter, valueUpdate: 'afterkeydown'"/></div>
-          </li>
+      <!-- ko if: selectedDatabase() != null -->
+        <li data-bind="slideVisible: selectedDatabase() != null && selectedDatabase().hasEntries() && options.isSearchVisible()">
+          <div><input type="text" placeholder="${ _('Table name...') }" style="width:90%;" data-bind="value: filter, valueUpdate: 'afterkeydown'"/></div>
+        </li>
 
-        <div class="table-container">
-          <div class="center" data-bind="visible: selectedDatabase() != null && selectedDatabase().loading()">
-            <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i><!--<![endif]-->
-            <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }"/><![endif]-->
-          </div>
-          <!-- ko template: { if: selectedDatabase() != null, name: 'assist-entries', data: selectedDatabase } --><!-- /ko -->
-          </div>
-        <!-- /ko -->
-      </ul>
+      <div class="table-container">
+        <div class="center" data-bind="visible: selectedDatabase() != null && selectedDatabase().loading()">
+          <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i><!--<![endif]-->
+          <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }"/><![endif]-->
+        </div>
+        <!-- ko template: { if: selectedDatabase() != null, name: 'assist-entries', data: selectedDatabase } --><!-- /ko -->
+        </div>
+      <!-- /ko -->
     </div>
 
     <div id="assistQuickLook" class="modal hide fade">
@@ -248,22 +261,22 @@ from desktop.views import _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 && assistHelper.app == 'impala' -->
+        <!-- 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 && assistHelper.app == 'impala'), name: 'assist-panel-table-stats' } --><!-- /ko -->
-        <!-- ko template: {if: column != null && ! hasError() && ! (isComplexType && assistHelper.app == 'impala'), name: 'assist-panel-column-stats' } --><!-- /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">
     (function() {
-      function AssistEntry (definition, parent, assistPanel, filter) {
+      function AssistEntry (definition, parent, assistSource, filter) {
         var self = this;
         self.definition = definition;
 
-        self.assistPanel = assistPanel;
+        self.assistSource = assistSource;
         self.parent = parent;
         self.filter = filter;
 
@@ -309,7 +322,7 @@ from desktop.views import _ko
 
         // Defer this part to allow ko to react on empty entries and loading
         window.setTimeout(function() {
-          self.assistPanel.assistHelper.fetchPanelData(self.getHierarchy(), function(data) {
+          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({
@@ -390,7 +403,7 @@ from desktop.views import _ko
             }
             self.loading(false);
           }, function() {
-            self.assistPanel.hasErrors(true);
+            self.assistSource.hasErrors(true);
             self.loading(false);
           });
         }, 10);
@@ -398,7 +411,7 @@ from desktop.views import _ko
 
       AssistEntry.prototype.createEntry = function(definition) {
         var self = this;
-        return new AssistEntry(definition, self, self.assistPanel, null)
+        return new AssistEntry(definition, self, self.assistSource, null)
       };
 
       AssistEntry.prototype.getHierarchy = function () {
@@ -454,11 +467,11 @@ from desktop.views import _ko
         var tableName = hierarchy[1];
 
         $assistQuickLook.find(".tableName").text(self.definition.name);
-        $assistQuickLook.find(".tableLink").attr("href", "/metastore/table/" + self.assistPanel.assistHelper.activeDatabase() + "/" + tableName);
+        $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.assistPanel.assistHelper.fetchTableHtmlPreview(tableName, function(data) {
+        self.assistSource.assistHelper.fetchTableHtmlPreview(self.snippet, tableName, function(data) {
           $assistQuickLook.find(".loader").hide();
           $assistQuickLook.find(".sample").html(data);
         }, function(e) {
@@ -476,7 +489,7 @@ from desktop.views import _ko
 
         if (self.statsVisible()) {
           self.statsVisible(false);
-          self.assistPanel.analysisStats(null);
+          self.assistSource.analysisStats(null);
           return;
         }
 
@@ -486,21 +499,22 @@ from desktop.views import _ko
         var columnName = hierarchy.length == 3 ? hierarchy[2] : null;
 
         self.statsVisible(true);
-        self.assistPanel.analysisStats(new TableStats(databaseName, tableName, columnName, self.assistPanel.assistHelper, self.definition.type));
+        self.assistSource.analysisStats(new TableStats(databaseName, tableName, columnName, self.assistSource.assistHelper, self.definition.type));
 
-        var catchChange = self.assistPanel.analysisStats.subscribe(function(newValue) {
+        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));
-        window.setTimeout(self.assistPanel.refreshPosition, 20);
+        window.setTimeout(self.assistSource.refreshPosition, 20);
       };
 
-      function TableStats (database, table, column, assistHelper, type) {
+      function TableStats (snippet, database, table, column, assistHelper, type) {
         var self = this;
 
+        self.snippet = snippet;
         self.database = database;
         self.table = table;
         self.column = column;
@@ -535,7 +549,7 @@ from desktop.views import _ko
         var self = this;
         self.loading(true);
         self.hasError(false);
-        self.assistHelper.fetchStats(self.table, self.column != null ? self.column : null, function (data) {
+        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;
@@ -572,7 +586,7 @@ from desktop.views import _ko
         var shouldFetchTerms = self.termsTabActive() || self.terms().length > 0;
         self.refreshing(true);
 
-        self.assistHelper.refreshTableStats(self.table, self.column, function() {
+        self.assistHelper.refreshTableStats(self.snippet, self.table, self.column, function() {
           self.refreshing(false);
           self.fetchData();
           if (shouldFetchTerms) {
@@ -586,12 +600,12 @@ from desktop.views import _ko
 
       TableStats.prototype.fetchTerms = function () {
         var self = this;
-        if (self.column == null || (self.isComplexType && self.assistHelper.app == "impala")) {
+        if (self.column == null || (self.isComplexType && self.snippet.type() == "impala")) {
           return;
         }
 
         self.loadingTerms(true);
-        self.assistHelper.fetchTerms(self.table, self.column, self.prefixFilter(), function (data) {
+        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 {
@@ -615,7 +629,37 @@ from desktop.views import _ko
       };
 
       function AssistPanel(params) {
+        var notebookViewModel = params.notebookViewModel;
+        var notebook = notebookViewModel.selectedNotebook();
+
+        self.sourceIndex = {};
+        $.each(notebookViewModel.availableSnippets(), function (index, snippet) {
+          var settings = notebookViewModel.getSnippetViewSettings(snippet.type());
+          var fakeSnippet = new Snippet(notebookViewModel, notebook, {
+            type: snippet.type(),
+            result: {}
+          });
+          if (settings.sqlDialect) {
+            self.sourceIndex[snippet.name()] = new AssistSource(fakeSnippet);
+          }
+        });
+
+        self.availableSourceTypes = Object.keys(self.sourceIndex);
+
+        self.selectedSourceType = ko.observable();
+        self.selectedSource = ko.observable();
+
+        self.selectedSourceType.subscribe(function (newSourceType) {
+          self.selectedSource(self.sourceIndex[newSourceType]);
+        });
+
+        self.selectedSourceType(self.availableSourceTypes[0]);
+      }
+
+      function AssistSource(snippet) {
         var self = this;
+        self.snippet = snippet;
+        self.assistHelper = snippet.getAssistHelper();
 
         self.hasErrors = ko.observable(false);
         self.simpleStyles = ko.observable(false);
@@ -629,19 +673,16 @@ from desktop.views import _ko
         self.options = ko.mapping.fromJS($.extend({
           isSearchVisible: false,
           lastSelectedDb: null
-        }, $.totalStorage(params.appName + ".assist.options") || {}));
+        }, $.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(params.appName + ".assist.options", ko.mapping.toJS(self.options))
+              $.totalStorage(snippet.type() + ".assist.options", ko.mapping.toJS(self.options))
             });
           }
         });
 
-        self.assistHelper = params.assistHelper;
-        self.app = self.assistHelper.options.app;
-
         self.databases = ko.observableArray();
         self.selectedDatabase = ko.observable();
 
@@ -652,7 +693,7 @@ from desktop.views import _ko
           return typeof self.selectedDatabase() != "undefined" && self.selectedDatabase() !== null && self.selectedDatabase().loading();
         });
 
-        self.selectedDatabase.subscribe(function(newValue) {
+        self.selectedDatabase.subscribe(function (newValue) {
           if (newValue != null && !newValue.hasEntries() && !newValue.loading()) {
               newValue.loadEntries()
           }
@@ -719,7 +760,7 @@ from desktop.views import _ko
         };
       }
 
-      AssistPanel.prototype.setDatabase = function(name) {
+      AssistSource.prototype.setDatabase = function(name) {
         var self = this;
         if (name == null) {
           return;
@@ -731,26 +772,26 @@ from desktop.views import _ko
         }));
       };
 
-      AssistPanel.prototype.toggleSearch = function () {
+      AssistSource.prototype.toggleSearch = function () {
         var self = this;
         self.options.isSearchVisible(!self.options.isSearchVisible());
       };
 
-      AssistPanel.prototype.reloadAssist = function() {
+      AssistSource.prototype.reloadAssist = function() {
         var self = this;
         self.reloading(true);
         self.loadingDatabases(true);
         self.selectedDatabase(null);
-        self.assistHelper.clearCache();
+        self.assistHelper.clearCache(self.snippet);
         self.fetchDatabases(function() {
           self.setDatabase(self.options.lastSelectedDb());
         });
       };
 
-      AssistPanel.prototype.fetchDatabases = function(callback) {
+      AssistSource.prototype.fetchDatabases = function(callback) {
         var self = this;
 
-        self.assistHelper.fetchDatabases(function(data) {
+        self.assistHelper.fetchDatabases(self.snippet, function(data) {
           self.databases($.map(data.databases, function(databaseName) {
             return new AssistEntry({
               name: databaseName,
@@ -767,7 +808,9 @@ from desktop.views import _ko
             callback();
           }
         }, function(message) {
-          if (message) {
+          if (message.statusText) {
+            $(document).trigger("error", "${ _('There was a problem loading the databases') }: " + message.statusText);
+          } else if (message) {
             $(document).trigger("error", message);
           } else {
             $(document).trigger("error", "${ _('There was a problem loading the databases.') }");

+ 25 - 6
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -16,8 +16,8 @@
 
 
 var SPARK_MAPPING = {
-  ignore: ["ace", "images", "autocompleter", "selectedStatement"]
-}
+  ignore: ["ace", "images", "autocompleter", "selectedStatement", "assistHelpers", "user"]
+};
 
 var Result = function (snippet, result) {
   var self = this;
@@ -157,6 +157,10 @@ var Snippet = function (vm, notebook, snippet) {
     return vm.getSnippetViewSettings(self.type()).placeHolder;
   };
 
+  self.getAssistHelper = function() {
+    return notebook.getAssistHelper(self.type());
+  };
+
   self.statement_raw = ko.observable(typeof snippet.statement_raw != "undefined" && snippet.statement_raw != null ? snippet.statement_raw : '');
   self.selectedStatement = ko.observable('');
   self.codemirrorSize = ko.observable(typeof snippet.codemirrorSize != "undefined" && snippet.codemirrorSize != null ? snippet.codemirrorSize : 100);
@@ -615,9 +619,12 @@ var Snippet = function (vm, notebook, snippet) {
   };
 
   self.autocompleter = new Autocompleter({
-    notebook: notebook,
     snippet: self,
-    assistHelper: vm.assistHelper
+    assistHelper: self.getAssistHelper()
+  });
+
+  self.type.subscribe(function () {
+    self.autocompleter.setAssistHelper(self.getAssistHelper());
   });
 
   self.init = function () {
@@ -673,6 +680,18 @@ var Notebook = function (vm, notebook) {
     }
   });
 
+  self.assistHelpers = {};
+
+  self.getAssistHelper = function (snippetType) {
+    if (! self.assistHelpers[snippetType]) {
+      self.assistHelpers[snippetType] = new AssistHelper({
+        notebook: self,
+        user: vm.user,
+      });
+    }
+    return self.assistHelpers[snippetType]
+  };
+
   self.getSession = function (session_type) {
     var _s = null;
     $.each(self.sessions(), function (index, s) {
@@ -908,6 +927,7 @@ var Notebook = function (vm, notebook) {
 function EditorViewModel(notebooks, options) {
   var self = this;
 
+  self.user = options.user;
   self.notebooks = ko.observableArray();
   self.selectedNotebook = ko.observable();
   self.combinedContent = ko.observable();
@@ -965,7 +985,6 @@ function EditorViewModel(notebooks, options) {
     }
   };
 
-  self.assistHelper = options.assistHelper;
   self.assistAvailable = ko.observable(options.assistAvailable);
 
   self.isLeftPanelVisible = ko.observable(self.assistAvailable() && $.totalStorage('spark_left_panel_visible') != null && $.totalStorage('spark_left_panel_visible'));
@@ -976,7 +995,7 @@ function EditorViewModel(notebooks, options) {
 
   self.availableSnippets = ko.mapping.fromJS(options.languages);
 
-  self.getSnippetViewSettings = function(snippetType) {
+  self.getSnippetViewSettings = function (snippetType) {
     if (options.snippetViewSettings[snippetType]) {
       return options.snippetViewSettings[snippetType];
     }

+ 5 - 8
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -263,7 +263,10 @@ from desktop.views import _ko
     <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { wasAssistVisible = false; $root.isLeftPanelVisible(false) }">
       <i class="fa fa-chevron-left"></i>
     </a>
-    <div class="assist" data-bind="component: { name: 'assist-panel', params: { assistHelper: assistHelper, appName: 'notebook'  }}"></div>
+    <div class="assist" data-bind="component: {
+        name: 'assist-panel',
+        params: { notebookViewModel: $root }
+      }"></div>
   </div>
   <div class="resizer" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable(), splitDraggable : { appName: 'notebook', leftPanelVisible: $root.isLeftPanelVisible }"><div class="resize-bar">&nbsp;</div></div>
   <div class="right-panel" data-bind="event: { scroll: function(){ $(document).trigger('hideAutocomplete'); } }">
@@ -466,7 +469,6 @@ from desktop.views import _ko
     <div class="editor span12" data-bind="verticalSlide: codeVisible">
       <div class="ace-editor" data-bind="attr: { id: id() }, delayedOverflow, aceEditor: {
           snippet: $data,
-          assistHelper: assistHelper,
           openIt: '${ _ko("Alt or Ctrl + Click to open it") }'
           }"></div>
       </div>
@@ -1818,13 +1820,8 @@ from desktop.views import _ko
     $('#snippet_' + snippet.id()).find('.download-form').submit();
   }
 
-  var assistHelper = new AssistHelper({
-    app: 'beeswax',
-    user: '${user}'
-  });
-
   var vmOptions = ${ options_json | n,unicode };
-  vmOptions.assistHelper = assistHelper;
+  vmOptions.user = '${user.username}';
   vmOptions.assistAvailable = "${ autocomplete_base_url | n,unicode }" !== "";
   vmOptions.snippetViewSettings = SNIPPET_VIEW_SETTINGS;