Эх сурвалжийг харах

[metastore] Added assist to import wizard pages

Enrico Berti 10 жил өмнө
parent
commit
de90643

+ 3 - 3
apps/beeswax/src/beeswax/create_table.py

@@ -180,7 +180,7 @@ def import_wizard(request, database='default'):
                                                             (s2_delim_form.cleaned_data['delimiter'],))
 
       if do_s2_auto_delim or do_s2_user_delim or cancel_s3_column_def:
-        return render('choose_delimiter.mako', request, {
+        return render('import_wizard_choose_delimiter.mako', request, {
           'action': reverse(app_name + ':import_wizard', kwargs={'database': database}),
           'delim_readable': DELIMITER_READABLE.get(s2_delim_form['delimiter'].data[0], s2_delim_form['delimiter'].data[1]),
           'initial': delim_is_auto,
@@ -210,7 +210,7 @@ def import_wizard(request, database='default'):
           if fields_list_for_json:
             fields_list_for_json[0] = map(lambda a: re.sub('[^\w]', '', a), fields_list_for_json[0]) # Cleaning headers
 
-          return render('define_columns.mako', request, {
+          return render('import_wizard_define_columns.mako', request, {
             'action': reverse(app_name + ':import_wizard', kwargs={'database': database}),
             'file_form': s1_file_form,
             'delim_form': s2_delim_form,
@@ -253,7 +253,7 @@ def import_wizard(request, database='default'):
   else:
     s1_file_form = CreateByImportFileForm()
 
-  return render('choose_file.mako', request, {
+  return render('import_wizard_choose_file.mako', request, {
     'action': reverse(app_name + ':import_wizard', kwargs={'database': database}),
     'file_form': s1_file_form,
     'database': database,

+ 0 - 192
apps/beeswax/src/beeswax/templates/choose_delimiter.mako

@@ -1,192 +0,0 @@
-## 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.views import commonheader, commonfooter
-from django.utils.translation import ugettext as _
-%>
-
-<%namespace name="comps" file="beeswax_components.mako" />
-<%namespace name="util" file="util.mako" />
-<%namespace name="layout" file="layout.mako" />
-
-${ commonheader(_('Create table from file'), 'metastore', user) | n,unicode }
-${ layout.metastore_menubar() }
-
-<link rel="stylesheet" href="${ static('metastore/css/metastore.css') }">
-
-<div class="container-fluid">
-    <div class="row-fluid">
-        <div class="span3">
-            <div class="sidebar-nav">
-                <ul class="nav nav-list">
-                  <li class="nav-header">${_('database')}</li>
-                  <li class="white">
-                      <select id="chooseDatabase" class="input-medium">
-                    % for db in databases:
-                      <option value="${db["url"]}"
-                              %if database==db["name"]:
-                                selected="selected"
-                              %endif
-                          >${db["name"]}</option>
-                    % endfor
-                      </select>
-                  </li>
-                  <li class="nav-header">${_('Actions')}</li>
-                  <li><a href="${ url(app_name + ':import_wizard', database=database)}"><i class="fa fa-files-o"></i> ${_('Create a new table from a file')}</a></li>
-                  <li><a href="${ url(app_name + ':create_table', database=database)}"><i class="fa fa-wrench"></i> ${_('Create a new table manually')}</a></li>
-                </ul>
-            </div>
-        </div>
-        <div class="span9">
-          <div class="card card-small" style="margin-top: 0">
-            <h1 class="card-heading simple">
-              <ul id="breadcrumbs" class="nav nav-pills hueBreadcrumbBar">
-                <li>
-                  <a href="${url('metastore:databases')}">${_('Databases')}</a><span class="divider">&gt;</span>
-                </li>
-                <li>
-                  <a href="${ url('metastore:show_tables', database=database) }">${database}</a><span class="divider">&gt;</span>
-                </li>
-                <li>
-                    <span style="padding-left:12px">${_('Create a new table from a file')}</span>
-                </li>
-              </ul>
-            </h1>
-            <div class="card-body">
-              <p>
-                <ul class="nav nav-pills">
-                <li><a id="step1" href="#">${_('Step 1: Choose File')}</a></li>
-                <li class="active"><a href="#">${_('Step 2: Choose Delimiter')}</a></li>
-                <li><a id="step3" href="#">${_('Step 3: Define Columns')}</a></li>
-            </ul>
-                <form id="delimiterForm" action="${action}" method="POST" class="form-horizontal">
-                ${ csrf_token(request) | n,unicode }
-                <div class="hide">
-                    ${util.render_form(file_form)}
-                    ${comps.field(delim_form['file_type'])}
-                </div>
-                <fieldset>
-                    <div class="alert alert-info"><h3>${_('Choose a Delimiter')}</h3>
-                        % if initial:
-                            ${_('Beeswax has determined that this file is delimited by')} <strong>${delim_readable}</strong>.
-                        % endif
-                    </div>
-                    <div class="control-group">
-                        ${comps.bootstrapLabel(delim_form["delimiter"])}
-                        <div class="controls">
-                            ${comps.field(delim_form["delimiter"], render_default=True)}
-                            <input id="submit_preview" class="btn btn-info" type="submit" value="${_('Preview')}" name="submit_preview"/>
-                            <span class="help-block">
-                            ${_('Enter the column delimiter which must be a single character. Use syntax like "\\001" or "\\t" for special characters.')}
-                            </span>
-                        </div>
-                    </div>
-                    <div class="control-group">
-                        <label class="control-label">${_('Table preview')}</label>
-                        <div class="controls">
-                            <div class="scrollable">
-                                <table class="table table-striped table-condensed">
-                                    <thead>
-                                    <tr>
-                                      % for i in range(n_cols):
-                                          <th>col_${i+1}</th>
-                                      % endfor
-                                    </tr>
-                                    </thead>
-                                    <tbody>
-                                      % for row in fields_list:
-                                      <tr>
-                                        % for val in row:
-                                          ${ comps.getEllipsifiedCell(val, "left")}
-                                        % endfor
-                                      </tr>
-                                      % endfor
-                                    </tbody>
-                                </table>
-                            </div>
-                        </div>
-                    </div>
-                </fieldset>
-
-                <div class="form-actions" style="padding-left: 10px">
-                    <input class="btn" type="submit" value="${_('Previous')}" name="cancel_delim"/>
-                    <input class="btn btn-primary" type="submit" name="submit_delim" value="${_('Next')}" />
-                </div>
-            </form>
-              </p>
-            </div>
-          </div>
-        </div>
-    </div>
-</div>
-
-<style type="text/css">
-  .scrollable {
-    width: 100%;
-    overflow-x: auto;
-  }
-</style>
-
-<link rel="stylesheet" href="${ static('desktop/ext/chosen/chosen.min.css') }">
-<script src="${ static('desktop/ext/chosen/chosen.jquery.min.js') }" type="text/javascript" charset="utf-8"></script>
-
-<script type="text/javascript" charset="utf-8">
-  $(document).ready(function () {
-    $("#chooseDatabase").chosen({
-      disable_search_threshold: 5,
-      width: "100%",
-      no_results_text: "${_('Oops, no database found!')}"
-    });
-
-    $("#chooseDatabase").chosen().change(function () {
-      window.location.href = $("#chooseDatabase").val();
-    });
-
-    $("[rel='tooltip']").tooltip();
-
-    $(".scrollable").width($(".form-actions").width() - 170);
-
-    $("#id_delimiter_1").css("margin-left", "4px").attr("placeholder", "${_('Type your delimiter here')}").hide();
-    $("#id_delimiter_0").change(function () {
-      if ($(this).val() == "__other__") {
-        $("#id_delimiter_1").show();
-      }
-      else {
-        $("#id_delimiter_1").hide();
-        $("#id_delimiter_1").val('');
-      }
-    });
-
-    $("#id_delimiter_0").change();
-
-    $("#step1").click(function (e) {
-      e.preventDefault();
-      $("input[name='cancel_delim']").click();
-    });
-    $("#step3").click(function (e) {
-      e.preventDefault();
-      $("input[name='submit_delim']").click();
-    });
-    $("body").keypress(function (e) {
-      if (e.which == 13) {
-        e.preventDefault();
-        $("input[name='submit_delim']").click();
-      }
-    });
-  });
-</script>
-
-${ commonfooter(messages) | n,unicode }

+ 0 - 217
apps/beeswax/src/beeswax/templates/choose_file.mako

@@ -1,217 +0,0 @@
-## 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.views import commonheader, commonfooter
-from django.utils.translation import ugettext as _
-%>
-
-<%namespace name="comps" file="beeswax_components.mako" />
-<%namespace name="layout" file="layout.mako" />
-
-${ commonheader(_('Create table from file'), 'metastore', user) | n,unicode }
-${ layout.metastore_menubar() }
-
-<link rel="stylesheet" href="${ static('metastore/css/metastore.css') }">
-
-<div class="container-fluid">
-    <div class="row-fluid">
-        <div class="span3">
-            <div class="sidebar-nav">
-                <ul class="nav nav-list">
-                  <li class="nav-header">${_('database')}</li>
-                  <li class="white">
-                      <select id="chooseDatabase" class="input-medium">
-                    % for db in databases:
-                      <option value="${db["url"]}"
-                              %if database==db["name"]:
-                                selected="selected"
-                              %endif
-                          >${db["name"]}</option>
-                    % endfor
-                      </select>
-                  </li>
-                  <li class="nav-header">${_('Actions')}</li>
-                  <li><a href="${ url(app_name + ':import_wizard', database=database)}"><i class="fa fa-files-o"></i> ${_('Create a new table from a file')}</a></li>
-                  <li><a href="${ url(app_name + ':create_table', database=database)}"><i class="fa fa-wrench"></i> ${_('Create a new table manually')}</a></li>
-                </ul>
-            </div>
-        </div>
-        <div class="span9">
-          <div class="card card-small" style="margin-top: 0">
-            <h1 class="card-heading simple">
-              <ul id="breadcrumbs" class="nav nav-pills hueBreadcrumbBar">
-                <li>
-                  <a href="${url('metastore:databases')}">${_('Databases')}</a><span class="divider">&gt;</span>
-                </li>
-                <li>
-                  <a href="${ url('metastore:show_tables', database=database) }">${database}</a><span class="divider">&gt;</span>
-                </li>
-                <li>
-                    <span style="padding-left:12px">${_('Create a new table from a file')}</span>
-                </li>
-              </ul>
-            </h1>
-            <div class="card-body">
-              <p>
-                <ul class="nav nav-pills">
-                <li class="active"><a href="${ url(app_name + ':import_wizard', database=database) }">${_('Step 1: Choose File')}</a></li>
-                <li><a id="step2" href="#">${_('Step 2: Choose Delimiter')}</a></li>
-                <li><a href="#">${_('Step 3: Define Columns')}</a></li>
-            </ul>
-                <form action="${action}" method="POST" class="form-horizontal">
-                ${ csrf_token(request) | n,unicode }
-                <fieldset>
-                    <div class="alert alert-info"><h3>${_('Name Your Table and Choose A File')}</h3></div>
-                    <div class="control-group">
-                        ${comps.bootstrapLabel(file_form["name"])}
-                        <div class="controls">
-                            ${comps.field(file_form["name"], placeholder=_('table_name'), show_errors=False)}
-                            <span  class="help-inline">${unicode(file_form["name"].errors) | n}</span>
-                        <span class="help-block">
-                            ${_('Name of the new table. Table names must be globally unique. Table names tend to correspond to the directory where the data will be stored.')}
-                        </span>
-                        </div>
-                    </div>
-                    <div class="control-group">
-                        ${comps.bootstrapLabel(file_form["comment"])}
-                        <div class="controls">
-                            ${comps.field(file_form["comment"],
-                            placeholder=_("Optional"),
-                            klass="",
-                            show_errors=False
-                            )}
-                            <span  class="help-inline">${unicode(file_form["comment"].errors) | n}</span>
-                        <span class="help-block">
-                        ${_("Use a table comment to describe the table.  For example, note the data's provenance and any caveats users need to know.")}
-                        </span>
-                        </div>
-                    </div>
-                    <div class="control-group">
-                        ${comps.bootstrapLabel(file_form["path"])}
-                        <div class="controls">
-                            ${comps.field(file_form["path"],
-                            placeholder="/user/user_name/data_dir",
-                            klass="pathChooser input-xxlarge",
-                            file_chooser=True,
-                            show_errors=False
-                            )}
-                            <span  class="help-inline">${unicode(file_form["path"].errors) | n}</span>
-                        <span class="help-block">
-                        ${_('The HDFS path to the file on which to base this new table definition. It can be compressed (gzip) or not.')}
-                        </span>
-                        </div>
-                    </div>
-                    <div class="control-group">
-                        ${comps.bootstrapLabel(file_form["do_import"])}
-                        <div class="controls">
-                            ${comps.field(file_form["do_import"], render_default=True)}
-                            <span class="help-block">
-                        ${_('Check this box to import the data in this file after creating the table definition. Leave it unchecked to define an empty table.')}
-                        <div id="fileWillBeMoved" class="alert">
-                            <strong>${_('Warning:')}</strong> ${_('The selected file is going to be moved during the import.')}
-                        </div>
-                        </span>
-                        </div>
-                    </div>
-                </fieldset>
-                <div class="form-actions" style="padding-left: 10px">
-                    <input type="submit" class="btn btn-primary" name="submit_file" value="${_('Next')}" />
-                </div>
-            </form>
-              </p>
-            </div>
-          </div>
-        </div>
-    </div>
-</div>
-
-
-<div id="chooseFile" class="modal hide fade">
-    <div class="modal-header">
-        <a href="#" class="close" data-dismiss="modal">&times;</a>
-        <h3>${_('Choose a file')}</h3>
-    </div>
-    <div class="modal-body">
-        <div id="filechooser">
-        </div>
-    </div>
-    <div class="modal-footer">
-    </div>
-</div>
-
-
-<style type="text/css">
-  #filechooser {
-    min-height: 100px;
-    overflow-y: auto;
-    margin-top: 10px;
-  }
-
-  #fileWillBeMoved {
-    margin-top: 10px;
-  }
-</style>
-
-<link rel="stylesheet" href="${ static('desktop/ext/chosen/chosen.min.css') }">
-<script src="${ static('desktop/ext/chosen/chosen.jquery.min.js') }" type="text/javascript" charset="utf-8"></script>
-
-<script type="text/javascript" charset="utf-8">
-  $(document).ready(function () {
-    $("#chooseDatabase").chosen({
-      disable_search_threshold: 5,
-      width: "100%",
-      no_results_text: "${_('Oops, no database found!')}"
-    });
-
-    $("#chooseDatabase").chosen().change(function () {
-      window.location.href = $("#chooseDatabase").val();
-    });
-
-    $(".fileChooserBtn").click(function (e) {
-      e.preventDefault();
-      var _destination = $(this).attr("data-filechooser-destination");
-      $("#filechooser").jHueFileChooser({
-        initialPath: $("input[name='" + _destination + "']").val(),
-        onFileChoose: function (filePath) {
-          $("input[name='" + _destination + "']").val(filePath);
-          $("#chooseFile").modal("hide");
-        },
-        createFolder: false
-      });
-      $("#chooseFile").modal("show");
-    });
-    $("#id_do_import").change(function () {
-      if ($(this).is(":checked")) {
-        $("#fileWillBeMoved").show();
-      }
-      else {
-        $("#fileWillBeMoved").hide();
-      }
-    });
-    $("#step2").click(function (e) {
-      e.preventDefault();
-      $("input[name='submit_file']").click();
-    });
-    $("body").keypress(function (e) {
-      if (e.which == 13) {
-        e.preventDefault();
-        $("input[name='submit_file']").click();
-      }
-    });
-  });
-</script>
-
-${ commonfooter(messages) | n,unicode }

+ 0 - 412
apps/beeswax/src/beeswax/templates/define_columns.mako

@@ -1,412 +0,0 @@
-## 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.views import commonheader, commonfooter
-from django.utils.translation import ugettext as _
-%>
-
-<%namespace name="comps" file="beeswax_components.mako" />
-<%namespace name="util" file="util.mako" />
-<%namespace name="layout" file="layout.mako" />
-
-${ commonheader(_('Create table from file'), 'metastore', user) | n,unicode }
-${ layout.metastore_menubar() }
-
-<link rel="stylesheet" href="${ static('metastore/css/metastore.css') }">
-
-<div class="container-fluid">
-    <div class="row-fluid">
-        <div class="span3">
-            <div class="sidebar-nav">
-                <ul class="nav nav-list">
-                  <li class="nav-header">${_('database')}</li>
-                  <li class="white">
-                      <select id="chooseDatabase" class="input-medium">
-                    % for db in databases:
-                      <option value="${db["url"]}"
-                              %if database==db["name"]:
-                                selected="selected"
-                              %endif
-                          >${db["name"]}</option>
-                    % endfor
-                      </select>
-                  </li>
-                  <li class="nav-header">${_('Actions')}</li>
-                  <li><a href="${ url(app_name + ':import_wizard', database=database)}"><i class="fa fa-files-o"></i> ${_('Create a new table from a file')}</a></li>
-                  <li><a href="${ url(app_name + ':create_table', database=database)}"><i class="fa fa-wrench"></i> ${_('Create a new table manually')}</a></li>
-                </ul>
-            </div>
-        </div>
-        <div class="span9">
-          <div class="card card-small" style="margin-top: 0">
-            <h1 class="card-heading simple">
-              <ul id="breadcrumbs" class="nav nav-pills hueBreadcrumbBar">
-                <li>
-                  <a href="${url('metastore:databases')}">${_('Databases')}</a><span class="divider">&gt;</span>
-                </li>
-                <li>
-                  <a href="${ url('metastore:show_tables', database=database) }">${database}</a><span class="divider">&gt;</span>
-                </li>
-                <li>
-                    <span style="padding-left:12px">${_('Create a new table from a file')}</span>
-                </li>
-              </ul>
-            </h1>
-            <div class="card-body">
-              <p>
-                <ul class="nav nav-pills">
-                <li><a id="step1" href="#">${_('Step 1: Choose File')}</a></li>
-                <li><a id="step2" href="#">${_('Step 2: Choose Delimiter')}</a></li>
-                <li class="active"><a href="#">${_('Step 3: Define Columns')}</a></li>
-            </ul>
-                <form action="${action}" method="POST" class="form-stacked">
-                ${ csrf_token(request) | n,unicode }
-                <div class="hide">
-                    ${util.render_form(file_form)}
-                    ${util.render_form(delim_form)}
-                    ${unicode(column_formset.management_form) | n}
-                </div>
-                <%
-                    n_rows = len(fields_list)
-                    if n_rows > 2: n_rows = 2
-                %>
-                <fieldset>
-                    <div class="alert alert-info">
-                      <h3>${_('Define your columns')}</h3>
-                    </div>
-                    <div class="row" style="margin-left: 8px">
-                      <div class="span3">
-                        <input id="removeHeader" type="checkbox" class="hide" name="removeHeader">
-                        ${_('Use first row as column names')} &nbsp;<a id="useHeader" class="btn disable-feedback"><i class="fa fa-outdent"></i></a>
-                      </div>
-                      <div class="span3">
-                        ${ _('Bulk edit column names') } &nbsp;<a id="editColumns" class="btn"><i class="fa fa-edit"></i></a>
-                      </div>
-                    </div>
-                    <div class="control-group" style="margin-top: 10px">
-                        <div class="controls">
-                            <div class="scrollable">
-                                <table class="table table-striped">
-                                    <thead>
-                                      <th id="column_names" style="width:210px">${ _('Column name') }</th>
-                                      <th style="width:210px">${ _('Column Type') }</th>
-                                      % for i in range(0, n_rows):
-                                        <th><em>${_('Sample Row')} #${i + 1}</em></th>
-                                      % endfor
-                                    </thead>
-                                    <tbody>
-                                      % for col, form in zip(range(len(column_formset.forms)), column_formset.forms):
-                                      <tr>
-                                        <td class="cols">
-                                          ${ comps.field(form["column_name"], render_default=False, placeholder=_("Column name")) }
-                                        </td>
-                                        <td>
-                                          ${ comps.field(form["column_type"], render_default=True) }
-                                          ${unicode(form["_exists"]) | n}
-                                        </td>
-                                        % for row in fields_list[:n_rows]:
-                                          ${ comps.getEllipsifiedCell(row[col], "bottom", "dataSample cols-%s" % (loop.index + 1)) }
-                                        % endfor
-                                      </tr>
-                                      %endfor
-                                    </tbody>
-                                </table>
-
-                            </div>
-                        </div>
-                    </div>
-                </fieldset>
-                <div class="form-actions" style="padding-left: 10px">
-                    <input class="btn" type="submit" name="cancel_create" value="${_('Previous')}" />
-                    <input class="btn btn-primary" type="submit" name="submit_create" value="${_('Create Table')}" />
-                </div>
-            </form>
-              </p>
-            </div>
-          </div>
-        </div>
-    </div>
-</div>
-
-<div id="columnNamesPopover" class="popover editable-container hide right">
-  <div class="arrow"></div>
-  <div class="popover-inner">
-    <h3 class="popover-title left">${ _('Write or paste comma separated column names') }</h3>
-    <div class="popover-content">
-      <p>
-        <div>
-          <form style="display: block;" class="form-inline editableform">
-            <div class="control-group">
-              <div>
-                <div style="position: relative;" class="editable-input">
-                  <input type="text" class="span8" style="padding-right: 24px;" placeholder="${ _('e.g. id, name, salary') }">
-                </div>
-                <div class="editable-buttons">
-                  <button type="button" class="btn btn-primary editable-submit"><i class="fa fa-check"></i></button>
-                  <button type="button" class="btn editable-cancel"><i class="fa fa-times"></i></button>
-                </div>
-              </div>
-            </div>
-          </form>
-        </div>
-      </p>
-    </div>
-  </div>
-</div>
-
-<link href="${ static('desktop/ext/css/bootstrap-editable.css') }" rel="stylesheet">
-
-<style type="text/css">
-  .scrollable {
-    width: 100%;
-    overflow-x: auto;
-  }
-
-  #editColumns {
-    cursor: pointer;
-  }
-</style>
-
-<link rel="stylesheet" href="${ static('desktop/ext/chosen/chosen.min.css') }">
-<script src="${ static('desktop/ext/chosen/chosen.jquery.min.js') }" type="text/javascript" charset="utf-8"></script>
-
-<script type="text/javascript" charset="utf-8">
-  $(document).ready(function () {
-    $("#chooseDatabase").chosen({
-      disable_search_threshold: 5,
-      width: "100%",
-      no_results_text: "${_('Oops, no database found!')}"
-    });
-
-    $("#chooseDatabase").chosen().change(function () {
-      window.location.href = $("#chooseDatabase").val();
-    });
-
-    $("[rel='tooltip']").tooltip();
-
-    $("#useHeader").on("click", function(){
-      var _isChecked = false;
-      var _klass = "btn-info";
-      if ($(this).hasClass(_klass)){
-        $(this).removeClass(_klass);
-      }
-      else {
-        $(this).addClass(_klass);
-        _isChecked = true;
-      }
-      $("#removeHeader").prop('checked', _isChecked);
-
-      $(".cols input[type='text']").each(function (cnt, item) {
-        if (_isChecked) {
-          $(item).data('previous', $(item).val());
-          $(item).val($.trim(${ fields_list_json | n,unicode }[0][cnt]));
-        } else {
-          $(item).val($(item).data('previous'));
-        }
-      });
-
-      $(".cols-1").each(function (cnt, item) {
-        if (_isChecked) {
-          $(item).data('previous', $(item).text());
-          $(item).text($.trim(${ fields_list_json | n,unicode }[1][cnt]));
-        } else {
-          $(item).text($(item).data('previous'));
-        }
-      });
-
-      $(".cols-2").each(function (cnt, item) {
-        if (_isChecked) {
-          $(item).data('previous', $(item).text());
-          $(item).text($.trim(${ fields_list_json | n,unicode }[2][cnt]));
-        } else {
-          $(item).text($(item).data('previous'));
-        }
-      });
-
-      guessColumnTypes();
-    });
-
-    guessColumnTypes();
-
-    // Really basic heuristic to detect if first row is a header.
-    var isString = 0;
-    $(".cols-1").each(function (cnt, item) {
-      if ($(item).data("possibleType") == 'string') {
-        isString += 1;
-      }
-    });
-    // First row is just strings
-    if (isString == $(".cols-1").length) {
-      $("#useHeader").click();
-    }
-
-    $(".scrollable").width($(".form-actions").width() - 10);
-
-    $("#step1").click(function (e) {
-      e.preventDefault();
-      $("input[name='cancel_create']").attr("name", "cancel_delim").click();
-    });
-
-    $("#step2").click(function (e) {
-      e.preventDefault();
-      $("input[name='cancel_create']").click();
-    });
-
-    $("body").keypress(function (e) {
-      if (e.which == 13) {
-        e.preventDefault();
-        $("input[name='submit_create']").click();
-      }
-    });
-
-    $("body").click(function () {
-      if ($("#columnNamesPopover").is(":visible")) {
-        $("#columnNamesPopover").hide();
-      }
-    });
-
-    $(".editable-container").click(function (e) {
-      e.stopImmediatePropagation();
-    });
-
-    $("#editColumns").click(function (e) {
-      e.stopImmediatePropagation();
-      $("[rel='tooltip']").tooltip("hide");
-      var _newVal = "";
-      $(".cols input[type='text']").each(function (cnt, item) {
-        _newVal += $(item).val() + (cnt < $(".cols input[type='text']").length - 1 ? ", " : "");
-      });
-      $("#columnNamesPopover").show().css("left", $("#column_names").position().left + 16).css("top", $("#column_names").position().top - ($("#columnNamesPopover").height() / 2));
-      $(".editable-input input").val(_newVal).focus();
-    });
-
-    $("#columnNamesPopover .editable-submit").click(function () {
-      parseEditable();
-    });
-
-    $(".editable-input input").keypress(function (e) {
-      if (e.keyCode == 13) {
-        parseEditable();
-        return false;
-      }
-    });
-
-    function parseEditable() {
-      parseJSON($(".editable-input input").val());
-      $("#columnNamesPopover").hide();
-      $(".editable-input input").val("");
-    }
-
-    $("#columnNamesPopover .editable-cancel").click(function () {
-      $("#columnNamesPopover").hide();
-    });
-
-    function guessColumnTypes() {
-      // Pick from 2nd column only
-      $(".dataSample").each(function () {
-        var _val = $.trim($(this).text());
-        var _field = $(this).siblings().find("select[id^=id_cols-]");
-        var _foundType = "string";
-
-        if ($.isNumeric(_val)) {
-          if (isInt(_val)) {
-            // it's an int
-            try {
-              // try to detect the size of the int
-              var _bytes = Math.ceil((Math.log(_val)/Math.log(2))/8);
-              switch (_bytes){
-                case 1:
-                  _foundType = "smallint";
-                  break;
-                case 2:
-                  _foundType = "smallint";
-                  break;
-                case 3:
-                case 4:
-                  _foundType = "int";
-                  break;
-                default:
-                  _foundType = "bigint";
-                  break;
-              }
-            }
-            catch (e){
-              _foundType = "int";
-            }
-          }
-          else {
-            // it's possibly a float
-            try {
-              _foundType = "float";
-              // try to detect the size of the int
-              var _bytes = Math.ceil((Math.log(_val)/Math.log(2))/8);
-              if (_bytes > 4){
-                _foundType = "double";
-              }
-            }
-            catch (e){
-              _foundType = "float";
-            }
-          }
-        }
-        else {
-          if (_val.toLowerCase().indexOf("true") > -1 || _val.toLowerCase().indexOf("false") > -1) {
-            // it's a boolean
-            _foundType = "boolean";
-          }
-          else {
-            // it's most probably a string
-            _foundType = "string";
-          }
-        }
-
-        _field.data("possibleType", _foundType);
-        $(this).data("possibleType", _foundType);
-      });
-
-      $("select[id^=id_cols-]").each(function () {
-        $(this).val($(this).data("possibleType"));
-      });
-    }
-
-    function parseJSON(val) {
-      try {
-        if (val.indexOf("\"") == -1 && val.indexOf("'") == -1) {
-          val = '"' + val.replace(/,/gi, '","') + '"';
-        }
-        if (val.indexOf("[") == -1) {
-          val = "[" + val + "]";
-        }
-        var _parsed = JSON.parse(val);
-        $(".cols input[type='text']").each(function (cnt, item) {
-          try {
-            $(item).val($.trim(_parsed[cnt]));
-          }
-          catch (i_err) {
-          }
-        });
-      }
-      catch (err) {
-      }
-    }
-
-    function isInt(n) {
-      return Math.floor(n) == n && n.toString().indexOf(".") == -1;
-    }
-  });
-</script>
-
-${ commonfooter(messages) | n,unicode }

+ 280 - 0
apps/beeswax/src/beeswax/templates/import_wizard_choose_delimiter.mako

@@ -0,0 +1,280 @@
+## 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.views import commonheader, commonfooter
+from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="comps" file="beeswax_components.mako" />
+<%namespace name="util" file="util.mako" />
+<%namespace name="layout" file="layout.mako" />
+<%namespace name="assist" file="/assist.mako" />
+<%namespace name="tableStats" file="/table_stats.mako" />
+<%namespace name="require" file="/require.mako" />
+
+
+${ commonheader(_('Create table from file'), 'metastore', user) | n,unicode }
+${ layout.metastore_menubar() }
+
+${ require.config() }
+
+${ tableStats.tableStats() }
+${ assist.assistPanel() }
+
+
+<script src="${ static('desktop/ext/js/d3.v3.js') }" type="text/javascript" charset="utf-8"></script>
+
+<link rel="stylesheet" href="${ static('metastore/css/metastore.css') }" type="text/css">
+<link rel="stylesheet" href="${ static('notebook/css/notebook.css') }">
+<style type="text/css">
+% if conf.CUSTOM.BANNER_TOP_HTML.get():
+  .show-assist {
+    top: 110px!important;
+  }
+  .main-content {
+    top: 112px!important;
+  }
+% endif
+</style>
+
+
+<a title="${_('Toggle Assist')}" class="pointer show-assist" data-bind="visible: !$root.isLeftPanelVisible() && $root.assistAvailable(), click: function() { $root.isLeftPanelVisible(true); }">
+  <i class="fa fa-chevron-right"></i>
+</a>
+
+
+<div class="main-content">
+  <div class="vertical-full container-fluid">
+    <div class="vertical-full">
+      <div class="vertical-full row-fluid panel-container">
+
+        <div class="assist-container left-panel" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable()">
+          <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { $root.isLeftPanelVisible(false) }">
+            <i class="fa fa-chevron-left"></i>
+          </a>
+          <div class="assist" data-bind="component: {
+              name: 'assist-panel',
+              params: {
+                sourceTypes: [{
+                  name: 'hive',
+                  type: 'hive'
+                }],
+                user: '${user.username}',
+                navigationSettings: {
+                  openItem: true,
+                  showPreview: true,
+                  showStats: false
+                }
+              }
+            }"></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">
+
+          <div class="metastore-main">
+            <h3>
+              <div class="inline-block pull-right" style="margin-top: -8px">
+                <a href="${ url('beeswax:import_wizard', database=database) }" title="${_('Create a new table from a file')}" class="inactive-action"><i class="fa fa-files-o"></i></a>
+                <a href="${ url('beeswax:create_table', database=database) }" title="${_('Create a new table manually')}" class="inactive-action margin-left-10"><i class="fa fa-wrench"></i></a>
+              </div>
+
+              <ul id="breadcrumbs" class="nav nav-pills hueBreadcrumbBar">
+                <li>
+                  <a href="${url('metastore:databases')}">${_('Databases')}</a><span class="divider">&gt;</span>
+                </li>
+                <li>
+                  <a href="${ url('metastore:show_tables', database=database) }">${database}</a><span class="divider">&gt;</span>
+                </li>
+                <li>
+                    <span style="padding-left:12px">${_('Create a new table from a file')}</span>
+                </li>
+              </ul>
+            </h3>
+
+            <ul class="nav nav-pills">
+                <li><a id="step1" href="#">${_('Step 1: Choose File')}</a></li>
+                <li class="active"><a href="#">${_('Step 2: Choose Delimiter')}</a></li>
+                <li><a id="step3" href="#">${_('Step 3: Define Columns')}</a></li>
+            </ul>
+            <form id="delimiterForm" action="${action}" method="POST" class="form-horizontal">
+              ${ csrf_token(request) | n,unicode }
+              <div class="hide">
+                  ${util.render_form(file_form)}
+                  ${comps.field(delim_form['file_type'])}
+              </div>
+              <fieldset>
+                  <div class="alert alert-info margin-top-10"><h3>${_('Choose a Delimiter')}</h3>
+                      % if initial:
+                          ${_('Beeswax has determined that this file is delimited by')} <strong>${delim_readable}</strong>.
+                      % endif
+                  </div>
+                  <div class="control-group">
+                      ${comps.bootstrapLabel(delim_form["delimiter"])}
+                      <div class="controls">
+                          ${comps.field(delim_form["delimiter"], render_default=True)}
+                          <input id="submit_preview" class="btn btn-info" type="submit" value="${_('Preview')}" name="submit_preview"/>
+                          <span class="help-block">
+                          ${_('Enter the column delimiter which must be a single character. Use syntax like "\\001" or "\\t" for special characters.')}
+                          </span>
+                      </div>
+                  </div>
+                  <div class="control-group">
+                      <label class="control-label">${_('Table preview')}</label>
+                      <div class="controls">
+                          <div class="scrollable">
+                              <table class="table table-striped table-condensed">
+                                  <thead>
+                                  <tr>
+                                    % for i in range(n_cols):
+                                        <th>col_${i+1}</th>
+                                    % endfor
+                                  </tr>
+                                  </thead>
+                                  <tbody>
+                                    % for row in fields_list:
+                                    <tr>
+                                      % for val in row:
+                                        ${ comps.getEllipsifiedCell(val, "left")}
+                                      % endfor
+                                    </tr>
+                                    % endfor
+                                  </tbody>
+                              </table>
+                          </div>
+                      </div>
+                  </div>
+              </fieldset>
+
+              <div class="form-actions" style="padding-left: 10px">
+                  <input class="btn" type="submit" value="${_('Previous')}" name="cancel_delim"/>
+                  <input class="btn btn-primary" type="submit" name="submit_delim" value="${_('Next')}" />
+              </div>
+            </form>
+
+          </div>
+
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+
+
+
+<style type="text/css">
+  .scrollable {
+    width: 100%;
+    overflow-x: auto;
+  }
+</style>
+
+<script type="text/javascript" charset="utf-8">
+
+  require([
+    "knockout",
+    "ko.charts",
+    "desktop/js/assist/assistHelper",
+    "assistPanel",
+    "tableStats",
+    "knockout-mapping",
+    "knockout-sortable",
+    "knockout-deferred-updates",
+    "ko.editable",
+    "ko.hue-bindings"
+  ], function (ko, charts, AssistHelper) {
+
+
+    function MetastoreViewModel(options) {
+      var self = this;
+      self.assistAvailable = ko.observable(true);
+      self.isLeftPanelVisible = ko.observable(self.assistAvailable() && $.totalStorage('spark_left_panel_visible') != null && $.totalStorage('spark_left_panel_visible'));
+
+      self.assistHelper = new AssistHelper(options);
+
+      huePubSub.subscribe("assist.table.selected", function (tableDef) {
+        location.href = '/metastore/table/' + tableDef.database + '/' + tableDef.name;
+      });
+
+      huePubSub.subscribe("assist.database.selected", function (databaseDef) {
+        location.href = '/metastore/tables/' + databaseDef.name;
+      });
+
+      self.isLeftPanelVisible.subscribe(function (newValue) {
+        $.totalStorage('spark_left_panel_visible', newValue);
+      });
+    }
+
+    $(document).ready(function () {
+
+      var options = {
+        user: '${ user.username }',
+        i18n: {
+          errorLoadingDatabases: "${ _('There was a problem loading the databases') }",
+          errorLoadingTablePreview: "${ _('There was a problem loading the table preview.') }"
+        }
+      }
+
+      var viewModel = new MetastoreViewModel(options);
+
+      ko.applyBindings(viewModel);
+
+      if (location.getParameter("error") != "") {
+        $.jHueNotify.error(location.getParameter("error"));
+      }
+
+
+      $("[rel='tooltip']").tooltip();
+
+      $(".scrollable").width($(".form-actions").width() - 170);
+
+      $("#id_delimiter_1").css("margin-left", "4px").attr("placeholder", "${_('Type your delimiter here')}").hide();
+      $("#id_delimiter_0").change(function () {
+        if ($(this).val() == "__other__") {
+          $("#id_delimiter_1").show();
+        }
+        else {
+          $("#id_delimiter_1").hide();
+          $("#id_delimiter_1").val('');
+        }
+      });
+
+      $("#id_delimiter_0").change();
+
+      $("#step1").click(function (e) {
+        e.preventDefault();
+        $("input[name='cancel_delim']").click();
+      });
+      $("#step3").click(function (e) {
+        e.preventDefault();
+        $("input[name='submit_delim']").click();
+      });
+      $("body").keypress(function (e) {
+        if (e.which == 13) {
+          e.preventDefault();
+          $("input[name='submit_delim']").click();
+        }
+      });
+
+    });
+  });
+
+</script>
+
+
+${ commonfooter(messages) | n,unicode }

+ 302 - 0
apps/beeswax/src/beeswax/templates/import_wizard_choose_file.mako

@@ -0,0 +1,302 @@
+## 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.views import commonheader, commonfooter
+from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="comps" file="beeswax_components.mako" />
+<%namespace name="layout" file="layout.mako" />
+<%namespace name="assist" file="/assist.mako" />
+<%namespace name="tableStats" file="/table_stats.mako" />
+<%namespace name="require" file="/require.mako" />
+
+
+${ commonheader(_('Create table from file'), 'metastore', user) | n,unicode }
+${ layout.metastore_menubar() }
+
+${ require.config() }
+
+${ tableStats.tableStats() }
+${ assist.assistPanel() }
+
+
+<script src="${ static('desktop/ext/js/d3.v3.js') }" type="text/javascript" charset="utf-8"></script>
+
+<link rel="stylesheet" href="${ static('metastore/css/metastore.css') }" type="text/css">
+<link rel="stylesheet" href="${ static('notebook/css/notebook.css') }">
+<style type="text/css">
+% if conf.CUSTOM.BANNER_TOP_HTML.get():
+  .show-assist {
+    top: 110px!important;
+  }
+  .main-content {
+    top: 112px!important;
+  }
+% endif
+</style>
+
+
+<a title="${_('Toggle Assist')}" class="pointer show-assist" data-bind="visible: !$root.isLeftPanelVisible() && $root.assistAvailable(), click: function() { $root.isLeftPanelVisible(true); }">
+  <i class="fa fa-chevron-right"></i>
+</a>
+
+
+<div class="main-content">
+  <div class="vertical-full container-fluid">
+    <div class="vertical-full">
+      <div class="vertical-full row-fluid panel-container">
+
+        <div class="assist-container left-panel" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable()">
+          <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { $root.isLeftPanelVisible(false) }">
+            <i class="fa fa-chevron-left"></i>
+          </a>
+          <div class="assist" data-bind="component: {
+              name: 'assist-panel',
+              params: {
+                sourceTypes: [{
+                  name: 'hive',
+                  type: 'hive'
+                }],
+                user: '${user.username}',
+                navigationSettings: {
+                  openItem: true,
+                  showPreview: true,
+                  showStats: false
+                }
+              }
+            }"></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">
+
+          <div class="metastore-main">
+            <h3>
+              <div class="inline-block pull-right" style="margin-top: -8px">
+                <a href="${ url('beeswax:import_wizard', database=database) }" title="${_('Create a new table from a file')}" class="inactive-action"><i class="fa fa-files-o"></i></a>
+                <a href="${ url('beeswax:create_table', database=database) }" title="${_('Create a new table manually')}" class="inactive-action margin-left-10"><i class="fa fa-wrench"></i></a>
+              </div>
+
+              <ul id="breadcrumbs" class="nav nav-pills hueBreadcrumbBar">
+                <li>
+                  <a href="${url('metastore:databases')}">${_('Databases')}</a><span class="divider">&gt;</span>
+                </li>
+                <li>
+                  <a href="${ url('metastore:show_tables', database=database) }">${database}</a><span class="divider">&gt;</span>
+                </li>
+                <li>
+                    <span style="padding-left:12px">${_('Create a new table from a file')}</span>
+                </li>
+              </ul>
+            </h3>
+
+            <ul class="nav nav-pills">
+              <li class="active"><a href="${ url(app_name + ':import_wizard', database=database) }">${_('Step 1: Choose File')}</a></li>
+              <li><a id="step2" href="#">${_('Step 2: Choose Delimiter')}</a></li>
+              <li><a href="#">${_('Step 3: Define Columns')}</a></li>
+            </ul>
+            <form action="${action}" method="POST" class="form-horizontal">
+              ${ csrf_token(request) | n,unicode }
+              <fieldset>
+                <div class="alert alert-info margin-top-10"><h3>${_('Name Your Table and Choose A File')}</h3></div>
+                <div class="control-group">
+                    ${comps.bootstrapLabel(file_form["name"])}
+                    <div class="controls">
+                        ${comps.field(file_form["name"], placeholder=_('table_name'), show_errors=False)}
+                        <span  class="help-inline">${unicode(file_form["name"].errors) | n}</span>
+                    <span class="help-block">
+                        ${_('Name of the new table. Table names must be globally unique. Table names tend to correspond to the directory where the data will be stored.')}
+                    </span>
+                    </div>
+                </div>
+                <div class="control-group">
+                    ${comps.bootstrapLabel(file_form["comment"])}
+                    <div class="controls">
+                        ${comps.field(file_form["comment"],
+                        placeholder=_("Optional"),
+                        klass="",
+                        show_errors=False
+                        )}
+                        <span  class="help-inline">${unicode(file_form["comment"].errors) | n}</span>
+                    <span class="help-block">
+                    ${_("Use a table comment to describe the table.  For example, note the data's provenance and any caveats users need to know.")}
+                    </span>
+                    </div>
+                </div>
+                <div class="control-group">
+                    ${comps.bootstrapLabel(file_form["path"])}
+                    <div class="controls">
+                        ${comps.field(file_form["path"],
+                        placeholder="/user/user_name/data_dir",
+                        klass="pathChooser input-xxlarge",
+                        file_chooser=True,
+                        show_errors=False
+                        )}
+                        <span  class="help-inline">${unicode(file_form["path"].errors) | n}</span>
+                    <span class="help-block">
+                    ${_('The HDFS path to the file on which to base this new table definition. It can be compressed (gzip) or not.')}
+                    </span>
+                    </div>
+                </div>
+                <div class="control-group">
+                  ${comps.bootstrapLabel(file_form["do_import"])}
+                  <div class="controls">
+                    ${comps.field(file_form["do_import"], render_default=True)}
+                    <span class="help-block">
+                    ${_('Check this box to import the data in this file after creating the table definition. Leave it unchecked to define an empty table.')}
+                    <div id="fileWillBeMoved" class="alert">
+                        <strong>${_('Warning:')}</strong> ${_('The selected file is going to be moved during the import.')}
+                    </div>
+                  </span>
+                  </div>
+                </div>
+              </fieldset>
+              <div class="form-actions" style="padding-left: 10px">
+                  <input type="submit" class="btn btn-primary" name="submit_file" value="${_('Next')}" />
+              </div>
+            </form>
+
+          </div>
+
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+
+
+<div id="chooseFile" class="modal hide fade">
+    <div class="modal-header">
+        <a href="#" class="close" data-dismiss="modal">&times;</a>
+        <h3>${_('Choose a file')}</h3>
+    </div>
+    <div class="modal-body">
+        <div id="filechooser">
+        </div>
+    </div>
+    <div class="modal-footer">
+    </div>
+</div>
+
+
+<style type="text/css">
+  #filechooser {
+    min-height: 100px;
+    overflow-y: auto;
+    margin-top: 10px;
+  }
+
+  #fileWillBeMoved {
+    margin-top: 10px;
+  }
+</style>
+
+<script type="text/javascript" charset="utf-8">
+
+  require([
+    "knockout",
+    "ko.charts",
+    "desktop/js/assist/assistHelper",
+    "assistPanel",
+    "tableStats",
+    "knockout-mapping",
+    "knockout-sortable",
+    "knockout-deferred-updates",
+    "ko.editable",
+    "ko.hue-bindings"
+  ], function (ko, charts, AssistHelper) {
+
+
+    function MetastoreViewModel(options) {
+      var self = this;
+      self.assistAvailable = ko.observable(true);
+      self.isLeftPanelVisible = ko.observable(self.assistAvailable() && $.totalStorage('spark_left_panel_visible') != null && $.totalStorage('spark_left_panel_visible'));
+
+      self.assistHelper = new AssistHelper(options);
+
+      huePubSub.subscribe("assist.table.selected", function (tableDef) {
+        location.href = '/metastore/table/' + tableDef.database + '/' + tableDef.name;
+      });
+
+      huePubSub.subscribe("assist.database.selected", function (databaseDef) {
+        location.href = '/metastore/tables/' + databaseDef.name;
+      });
+
+      self.isLeftPanelVisible.subscribe(function (newValue) {
+        $.totalStorage('spark_left_panel_visible', newValue);
+      });
+    }
+
+    $(document).ready(function () {
+
+      var options = {
+        user: '${ user.username }',
+        i18n: {
+          errorLoadingDatabases: "${ _('There was a problem loading the databases') }",
+          errorLoadingTablePreview: "${ _('There was a problem loading the table preview.') }"
+        }
+      }
+
+      var viewModel = new MetastoreViewModel(options);
+
+      ko.applyBindings(viewModel);
+
+      if (location.getParameter("error") != "") {
+        $.jHueNotify.error(location.getParameter("error"));
+      }
+
+      $(".fileChooserBtn").click(function (e) {
+        e.preventDefault();
+        var _destination = $(this).attr("data-filechooser-destination");
+        $("#filechooser").jHueFileChooser({
+          initialPath: $("input[name='" + _destination + "']").val(),
+          onFileChoose: function (filePath) {
+            $("input[name='" + _destination + "']").val(filePath);
+            $("#chooseFile").modal("hide");
+          },
+          createFolder: false
+        });
+        $("#chooseFile").modal("show");
+      });
+      $("#id_do_import").change(function () {
+        if ($(this).is(":checked")) {
+          $("#fileWillBeMoved").show();
+        }
+        else {
+          $("#fileWillBeMoved").hide();
+        }
+      });
+      $("#step2").click(function (e) {
+        e.preventDefault();
+        $("input[name='submit_file']").click();
+      });
+      $("body").keypress(function (e) {
+        if (e.which == 13) {
+          e.preventDefault();
+          $("input[name='submit_file']").click();
+        }
+      });
+
+    });
+  });
+
+</script>
+
+
+${ commonfooter(messages) | n,unicode }

+ 494 - 0
apps/beeswax/src/beeswax/templates/import_wizard_define_columns.mako

@@ -0,0 +1,494 @@
+## 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.views import commonheader, commonfooter
+from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="comps" file="beeswax_components.mako" />
+<%namespace name="util" file="util.mako" />
+<%namespace name="layout" file="layout.mako" />
+<%namespace name="assist" file="/assist.mako" />
+<%namespace name="tableStats" file="/table_stats.mako" />
+<%namespace name="require" file="/require.mako" />
+
+
+${ commonheader(_('Create table from file'), 'metastore', user) | n,unicode }
+${ layout.metastore_menubar() }
+
+${ require.config() }
+
+${ tableStats.tableStats() }
+${ assist.assistPanel() }
+
+
+<script src="${ static('desktop/ext/js/d3.v3.js') }" type="text/javascript" charset="utf-8"></script>
+
+<link rel="stylesheet" href="${ static('metastore/css/metastore.css') }" type="text/css">
+<link rel="stylesheet" href="${ static('notebook/css/notebook.css') }">
+<style type="text/css">
+% if conf.CUSTOM.BANNER_TOP_HTML.get():
+  .show-assist {
+    top: 110px!important;
+  }
+  .main-content {
+    top: 112px!important;
+  }
+% endif
+</style>
+
+<a title="${_('Toggle Assist')}" class="pointer show-assist" data-bind="visible: !$root.isLeftPanelVisible() && $root.assistAvailable(), click: function() { $root.isLeftPanelVisible(true); }">
+  <i class="fa fa-chevron-right"></i>
+</a>
+
+
+<div class="main-content">
+  <div class="vertical-full container-fluid">
+    <div class="vertical-full">
+      <div class="vertical-full row-fluid panel-container">
+
+        <div class="assist-container left-panel" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable()">
+          <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { $root.isLeftPanelVisible(false) }">
+            <i class="fa fa-chevron-left"></i>
+          </a>
+          <div class="assist" data-bind="component: {
+              name: 'assist-panel',
+              params: {
+                sourceTypes: [{
+                  name: 'hive',
+                  type: 'hive'
+                }],
+                user: '${user.username}',
+                navigationSettings: {
+                  openItem: true,
+                  showPreview: true,
+                  showStats: false
+                }
+              }
+            }"></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">
+
+          <div class="metastore-main">
+            <h3>
+              <div class="inline-block pull-right" style="margin-top: -8px">
+                <a href="${ url('beeswax:import_wizard', database=database) }" title="${_('Create a new table from a file')}" class="inactive-action"><i class="fa fa-files-o"></i></a>
+                <a href="${ url('beeswax:create_table', database=database) }" title="${_('Create a new table manually')}" class="inactive-action margin-left-10"><i class="fa fa-wrench"></i></a>
+              </div>
+
+              <ul id="breadcrumbs" class="nav nav-pills hueBreadcrumbBar">
+                <li>
+                  <a href="${url('metastore:databases')}">${_('Databases')}</a><span class="divider">&gt;</span>
+                </li>
+                <li>
+                  <a href="${ url('metastore:show_tables', database=database) }">${database}</a><span class="divider">&gt;</span>
+                </li>
+                <li>
+                    <span style="padding-left:12px">${_('Create a new table from a file')}</span>
+                </li>
+              </ul>
+            </h3>
+            <ul class="nav nav-pills">
+              <li><a id="step1" href="#">${_('Step 1: Choose File')}</a></li>
+              <li><a id="step2" href="#">${_('Step 2: Choose Delimiter')}</a></li>
+              <li class="active"><a href="#">${_('Step 3: Define Columns')}</a></li>
+            </ul>
+            <form action="${action}" method="POST" class="form-stacked">
+              ${ csrf_token(request) | n,unicode }
+              <div class="hide">
+                  ${util.render_form(file_form)}
+                  ${util.render_form(delim_form)}
+                  ${unicode(column_formset.management_form) | n}
+              </div>
+              <%
+                  n_rows = len(fields_list)
+                  if n_rows > 2: n_rows = 2
+              %>
+              <fieldset>
+                <div class="alert alert-info margin-top-10">
+                  <h3>${_('Define your columns')}</h3>
+                </div>
+                <div class="row" style="margin-left: 8px">
+                  <div class="span3">
+                    <input id="removeHeader" type="checkbox" class="hide" name="removeHeader">
+                    ${_('Use first row as column names')} &nbsp;<a id="useHeader" class="btn disable-feedback"><i class="fa fa-outdent"></i></a>
+                  </div>
+                  <div class="span3">
+                    ${ _('Bulk edit column names') } &nbsp;<a id="editColumns" class="btn"><i class="fa fa-edit"></i></a>
+                  </div>
+                </div>
+                <div class="control-group" style="margin-top: 10px">
+                  <div class="controls">
+                    <div class="scrollable">
+                      <table class="table table-striped">
+                        <thead>
+                          <th id="column_names" style="width:210px">${ _('Column name') }</th>
+                          <th style="width:210px">${ _('Column Type') }</th>
+                          % for i in range(0, n_rows):
+                            <th><em>${_('Sample Row')} #${i + 1}</em></th>
+                          % endfor
+                        </thead>
+                        <tbody>
+                          % for col, form in zip(range(len(column_formset.forms)), column_formset.forms):
+                          <tr>
+                            <td class="cols">
+                              ${ comps.field(form["column_name"], render_default=False, placeholder=_("Column name")) }
+                            </td>
+                            <td>
+                              ${ comps.field(form["column_type"], render_default=True) }
+                              ${unicode(form["_exists"]) | n}
+                            </td>
+                            % for row in fields_list[:n_rows]:
+                              ${ comps.getEllipsifiedCell(row[col], "bottom", "dataSample cols-%s" % (loop.index + 1)) }
+                            % endfor
+                          </tr>
+                          %endfor
+                        </tbody>
+                      </table>
+                    </div>
+                  </div>
+                </div>
+              </fieldset>
+              <div class="form-actions" style="padding-left: 10px">
+                <input class="btn" type="submit" name="cancel_create" value="${_('Previous')}" />
+                <input class="btn btn-primary" type="submit" name="submit_create" value="${_('Create Table')}" />
+              </div>
+            </form>
+
+          </div>
+
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div id="columnNamesPopover" class="popover editable-container hide right">
+  <div class="arrow"></div>
+  <div class="popover-inner">
+    <h3 class="popover-title left">${ _('Write or paste comma separated column names') }</h3>
+    <div class="popover-content">
+      <p>
+        <div>
+          <form style="display: block;" class="form-inline editableform">
+            <div class="control-group">
+              <div>
+                <div style="position: relative;" class="editable-input">
+                  <input type="text" class="span8" style="padding-right: 24px;" placeholder="${ _('e.g. id, name, salary') }">
+                </div>
+                <div class="editable-buttons">
+                  <button type="button" class="btn btn-primary editable-submit"><i class="fa fa-check"></i></button>
+                  <button type="button" class="btn editable-cancel"><i class="fa fa-times"></i></button>
+                </div>
+              </div>
+            </div>
+          </form>
+        </div>
+      </p>
+    </div>
+  </div>
+</div>
+
+<link href="${ static('desktop/ext/css/bootstrap-editable.css') }" rel="stylesheet">
+
+<style type="text/css">
+  .scrollable {
+    width: 100%;
+    overflow-x: auto;
+  }
+
+  #editColumns {
+    cursor: pointer;
+  }
+</style>
+
+<script type="text/javascript" charset="utf-8">
+
+  require([
+    "knockout",
+    "ko.charts",
+    "desktop/js/assist/assistHelper",
+    "assistPanel",
+    "tableStats",
+    "knockout-mapping",
+    "knockout-sortable",
+    "knockout-deferred-updates",
+    "ko.editable",
+    "ko.hue-bindings"
+  ], function (ko, charts, AssistHelper) {
+
+
+    function MetastoreViewModel(options) {
+      var self = this;
+      self.assistAvailable = ko.observable(true);
+      self.isLeftPanelVisible = ko.observable(self.assistAvailable() && $.totalStorage('spark_left_panel_visible') != null && $.totalStorage('spark_left_panel_visible'));
+
+      self.assistHelper = new AssistHelper(options);
+
+      huePubSub.subscribe("assist.table.selected", function (tableDef) {
+        location.href = '/metastore/table/' + tableDef.database + '/' + tableDef.name;
+      });
+
+      huePubSub.subscribe("assist.database.selected", function (databaseDef) {
+        location.href = '/metastore/tables/' + databaseDef.name;
+      });
+
+      self.isLeftPanelVisible.subscribe(function (newValue) {
+        $.totalStorage('spark_left_panel_visible', newValue);
+      });
+    }
+
+    $(document).ready(function () {
+
+      var options = {
+        user: '${ user.username }',
+        i18n: {
+          errorLoadingDatabases: "${ _('There was a problem loading the databases') }",
+          errorLoadingTablePreview: "${ _('There was a problem loading the table preview.') }"
+        }
+      }
+
+      var viewModel = new MetastoreViewModel(options);
+
+      ko.applyBindings(viewModel);
+
+      if (location.getParameter("error") != "") {
+        $.jHueNotify.error(location.getParameter("error"));
+      }
+
+      $("[rel='tooltip']").tooltip();
+
+      $("#useHeader").on("click", function () {
+        var _isChecked = false;
+        var _klass = "btn-info";
+        if ($(this).hasClass(_klass)) {
+          $(this).removeClass(_klass);
+        }
+        else {
+          $(this).addClass(_klass);
+          _isChecked = true;
+        }
+        $("#removeHeader").prop('checked', _isChecked);
+
+        $(".cols input[type='text']").each(function (cnt, item) {
+          if (_isChecked) {
+            $(item).data('previous', $(item).val());
+            $(item).val($.trim(${ fields_list_json | n,unicode }[0][cnt]));
+          } else {
+            $(item).val($(item).data('previous'));
+          }
+        });
+
+        $(".cols-1").each(function (cnt, item) {
+          if (_isChecked) {
+            $(item).data('previous', $(item).text());
+            $(item).text($.trim(${ fields_list_json | n,unicode }[1][cnt]));
+          } else {
+            $(item).text($(item).data('previous'));
+          }
+        });
+
+        $(".cols-2").each(function (cnt, item) {
+          if (_isChecked) {
+            $(item).data('previous', $(item).text());
+            $(item).text($.trim(${ fields_list_json | n,unicode }[2][cnt]));
+          } else {
+            $(item).text($(item).data('previous'));
+          }
+        });
+
+        guessColumnTypes();
+      });
+
+      guessColumnTypes();
+
+      // Really basic heuristic to detect if first row is a header.
+      var isString = 0;
+      $(".cols-1").each(function (cnt, item) {
+        if ($(item).data("possibleType") == 'string') {
+          isString += 1;
+        }
+      });
+      // First row is just strings
+      if (isString == $(".cols-1").length) {
+        $("#useHeader").click();
+      }
+
+      $(".scrollable").width($(".form-actions").width() - 10);
+
+      $("#step1").click(function (e) {
+        e.preventDefault();
+        $("input[name='cancel_create']").attr("name", "cancel_delim").click();
+      });
+
+      $("#step2").click(function (e) {
+        e.preventDefault();
+        $("input[name='cancel_create']").click();
+      });
+
+      $("body").keypress(function (e) {
+        if (e.which == 13) {
+          e.preventDefault();
+          $("input[name='submit_create']").click();
+        }
+      });
+
+      $("body").click(function () {
+        if ($("#columnNamesPopover").is(":visible")) {
+          $("#columnNamesPopover").hide();
+        }
+      });
+
+      $(".editable-container").click(function (e) {
+        e.stopImmediatePropagation();
+      });
+
+      $("#editColumns").click(function (e) {
+        e.stopImmediatePropagation();
+        $("[rel='tooltip']").tooltip("hide");
+        var _newVal = "";
+        $(".cols input[type='text']").each(function (cnt, item) {
+          _newVal += $(item).val() + (cnt < $(".cols input[type='text']").length - 1 ? ", " : "");
+        });
+        $("#columnNamesPopover").show().css("left", $("#column_names").position().left + 16).css("top", $("#column_names").position().top - ($("#columnNamesPopover").height() / 2));
+        $(".editable-input input").val(_newVal).focus();
+      });
+
+      $("#columnNamesPopover .editable-submit").click(function () {
+        parseEditable();
+      });
+
+      $(".editable-input input").keypress(function (e) {
+        if (e.keyCode == 13) {
+          parseEditable();
+          return false;
+        }
+      });
+
+      function parseEditable() {
+        parseJSON($(".editable-input input").val());
+        $("#columnNamesPopover").hide();
+        $(".editable-input input").val("");
+      }
+
+      $("#columnNamesPopover .editable-cancel").click(function () {
+        $("#columnNamesPopover").hide();
+      });
+
+      function guessColumnTypes() {
+        // Pick from 2nd column only
+        $(".dataSample").each(function () {
+          var _val = $.trim($(this).text());
+          var _field = $(this).siblings().find("select[id^=id_cols-]");
+          var _foundType = "string";
+
+          if ($.isNumeric(_val)) {
+            if (isInt(_val)) {
+              // it's an int
+              try {
+                // try to detect the size of the int
+                var _bytes = Math.ceil((Math.log(_val) / Math.log(2)) / 8);
+                switch (_bytes) {
+                  case 1:
+                    _foundType = "smallint";
+                    break;
+                  case 2:
+                    _foundType = "smallint";
+                    break;
+                  case 3:
+                  case 4:
+                    _foundType = "int";
+                    break;
+                  default:
+                    _foundType = "bigint";
+                    break;
+                }
+              }
+              catch (e) {
+                _foundType = "int";
+              }
+            }
+            else {
+              // it's possibly a float
+              try {
+                _foundType = "float";
+                // try to detect the size of the int
+                var _bytes = Math.ceil((Math.log(_val) / Math.log(2)) / 8);
+                if (_bytes > 4) {
+                  _foundType = "double";
+                }
+              }
+              catch (e) {
+                _foundType = "float";
+              }
+            }
+          }
+          else {
+            if (_val.toLowerCase().indexOf("true") > -1 || _val.toLowerCase().indexOf("false") > -1) {
+              // it's a boolean
+              _foundType = "boolean";
+            }
+            else {
+              // it's most probably a string
+              _foundType = "string";
+            }
+          }
+
+          _field.data("possibleType", _foundType);
+          $(this).data("possibleType", _foundType);
+        });
+
+        $("select[id^=id_cols-]").each(function () {
+          $(this).val($(this).data("possibleType"));
+        });
+      }
+
+      function parseJSON(val) {
+        try {
+          if (val.indexOf("\"") == -1 && val.indexOf("'") == -1) {
+            val = '"' + val.replace(/,/gi, '","') + '"';
+          }
+          if (val.indexOf("[") == -1) {
+            val = "[" + val + "]";
+          }
+          var _parsed = JSON.parse(val);
+          $(".cols input[type='text']").each(function (cnt, item) {
+            try {
+              $(item).val($.trim(_parsed[cnt]));
+            }
+            catch (i_err) {
+            }
+          });
+        }
+        catch (err) {
+        }
+      }
+
+      function isInt(n) {
+        return Math.floor(n) == n && n.toString().indexOf(".") == -1;
+      }
+
+
+    });
+  });
+
+</script>
+
+
+${ commonfooter(messages) | n,unicode }

+ 1 - 1
desktop/core/src/desktop/static/desktop/js/assist/assistEntry.js

@@ -75,7 +75,7 @@
     });
 
     self.filteredEntries = ko.computed(function () {
-      if (self.filter == null || (self.filter.showTables() && self.filter.showViews() && self.filter.query().length === 0)) {
+      if (self.filter == null || (self.filter.showTables && self.filter.showTables() && self.filter.showViews() && self.filter.query().length === 0)) {
         return self.entries();
       }
       var result = [];