Pārlūkot izejas kodu

HUE-993 [impala] Support download of result

Bunch of small UI improvements for Imapala too.
Most of the is impala app test should be removed in 2.3 and moved
to a specific Impala app.
Hie 'Tables' tab in Impala
Romain Rigaux 13 gadi atpakaļ
vecāks
revīzija
1b89554

+ 1 - 1
apps/beeswax/src/beeswax/server/dbms.py

@@ -20,7 +20,7 @@ import thrift
 import time
 
 from django.utils.encoding import force_unicode
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext as _
 
 from filebrowser.views import location_to_url
 

+ 5 - 5
apps/beeswax/src/beeswax/templates/describe_table.mako

@@ -60,7 +60,7 @@ ${layout.menubar(section='tables')}
                 <ul class="nav nav-list">
                     <li class="nav-header">${_('Actions')}</li>
                     <li><a href="#importData" data-toggle="modal">${_('Import Data')}</a></li>
-                    <li><a href="${ url("beeswax:read_table", database=database, table=table.name) }">${_('Browse Data')}</a></li>
+                    <li><a href="${ url(app_name + ':read_table', database=database, table=table.name) }">${_('Browse Data')}</a></li>
                     <li><a href="#dropTable" data-toggle="modal">${_('Drop')} ${view_or_table_noun}</a></li>
                     <li><a href="${ table.hdfs_link }" rel="${ table.path_location }">${_('View File Location')}</a></li>
                 </ul>
@@ -89,7 +89,7 @@ ${layout.menubar(section='tables')}
                 % if table.partition_keys:
                   <div class="tab-pane" id="partitionColumns">
                     ${column_table(table.partition_keys)}
-                    <a href="${ url("beeswax:describe_partitions", database=database, table=table.name) }">${_('Show Partitions')}</a>
+                    <a href="${ url(app_name + ':describe_partitions', database=database, table=table.name) }">${_('Show Partitions')}</a>
                   </div>
                 % endif
 
@@ -131,7 +131,7 @@ ${layout.menubar(section='tables')}
 
 
 <div id="dropTable" class="modal hide fade">
-    <form id="dropTableForm" method="POST" action="${ url("beeswax:drop_table", database=database, table=table.name) }">
+    <form id="dropTableForm" method="POST" action="${ url(app_name + ':drop_table', database=database, table=table.name) }">
     <div class="modal-header">
         <a href="#" class="close" data-dismiss="modal">&times;</a>
         <h3>${_('Drop Table')}</h3>
@@ -150,7 +150,7 @@ ${layout.menubar(section='tables')}
 
 
 <div id="importData" class="modal hide fade">
-    <form method="POST" action="${ url("beeswax:load_table", database=database, table=table.name) }" class="form-horizontal">
+    <form method="POST" action="${ url(app_name + ':load_table', database=database, table=table.name) }" class="form-horizontal">
         <div class="modal-header">
             <a href="#" class="close" data-dismiss="modal">&times;</a>
             <h3>${_('Import data')}</h3>
@@ -250,7 +250,7 @@ ${layout.menubar(section='tables')}
        }
      });
 
-     $.getJSON("${ url("beeswax:drop_table", database=database, table=table.name) }", function(data) {
+     $.getJSON("${ url(app_name + ':drop_table', database=database, table=table.name) }", function(data) {
        $("#dropTableMessage").text(data.title);
      });
 

+ 21 - 5
apps/beeswax/src/beeswax/templates/execute.mako

@@ -43,6 +43,9 @@
 
     <div class="actions">
         <a id="executeQuery" class="btn btn-primary" tabindex="0">${_('Execute')}</a>
+        % if app_name == 'impala':
+          <a id="downloadQuery" class="btn">${_('Download')}</a>
+        % endif
         % if design and not design.is_auto and design.name:
         <a id="saveQuery" class="btn">${_('Save')}</a>
         % endif
@@ -90,8 +93,7 @@ ${layout.menubar(section='query')}
 
                                 <div class="control-group">
                                     ${comps.label(f['key'])}
-                                    ${comps.field(f['key'], attrs=dict(
-                                        placeholder="mapred.reduce.tasks",
+                                    ${comps.field(f['key'], attrs=dict(placeholder=app_name == 'impala' and "ABORT_ON_ERROR" or "mapred.reduce.tasks",
                                         klass="settingsField span8"
                                     ))}
                                 </div>
@@ -208,13 +210,19 @@ ${layout.menubar(section='query')}
                                 ${_("Enable Parameterization")}
                             </label>
                         </li>
-                        <li class="nav-header">${_('Email Notification')}</li>
-                        <li>
+                        <ul
+                        % if app_name == 'impala':
+                            class="hide"
+                        % endif
+                        >
+                          <li class="nav-header">${_('Email Notification')}</li>
+                          <li>
                             <label class="checkbox" rel="tooltip" data-original-title="${_("If checked, you will receive an email notification when the query completes.")}">
                                 <input type="checkbox" id="id_${form.query["email_notify"].html_name | n}" name="${form.query["email_notify"].html_name | n}" ${extract_field_data(form.query["email_notify"]) and "CHECKED" or ""}/>
                                 ${_("Email me on completion")}
                             </label>
-                        </li>
+                          </li>
+                        </ul>
                     </ul>
                     <input type="hidden" name="${form.query["query"].html_name | n}" class="query" value="" />
                 </form>
@@ -455,6 +463,14 @@ ${layout.menubar(section='query')}
             checkAndSubmit();
         });
 
+        % if app_name == 'impala':
+        $("#downloadQuery").click(function(){
+            $("<input>").attr("type", "hidden").attr("name", "button-submit").attr("value", "Execute").appendTo($("#advancedSettingsForm"));
+            $("<input>").attr("type", "hidden").attr("name", "download").attr("value", "true").appendTo($("#advancedSettingsForm"));
+            checkAndSubmit();
+        });
+        % endif
+
         $("#saveQuery").click(function(){
             $("<input>").attr("type","hidden").attr("name","saveform-name")
                 .attr("value", "${extract_field_data(form.saveform["name"])}").appendTo($("#advancedSettingsForm"));

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

@@ -35,7 +35,9 @@ def is_selected(section, matcher):
 			<li class="${is_selected(section, 'my queries')}"><a href="${ url(app_name + ':my_queries') }">${_('My Queries')}</a></li>
 			<li class="${is_selected(section, 'saved queries')}"><a href="${ url(app_name + ':list_designs') }">${_('Saved Queries')}</a></li>
 			<li class="${is_selected(section, 'history')}"><a href="${ url(app_name + ':list_query_history') }">${_('History')}</a></li>
+			% if app_name != 'impala':
 			<li class="${is_selected(section, 'tables')}"><a href="${ url(app_name + ':show_tables') }">${_('Tables')}</a></li>
+			% endif
 			<li class="${is_selected(section, 'configuration')}"><a href="${ url(app_name + ':configuration') }">${_('Settings')}</a></li>
 		</ul>
 	</div>

+ 1 - 1
apps/beeswax/src/beeswax/templates/list_history.mako

@@ -127,7 +127,7 @@ ${layout.menubar(section='history')}
                 <td>${query.owner}</td>
                 <td>${models.QueryHistory.STATE[query.last_state]}</td>
                 <td>
-                  % if qcontext and query.last_state != models.QueryHistory.STATE.expired.index:
+                  % if qcontext and query.last_state not in (models.QueryHistory.STATE.expired.index, models.QueryHistory.STATE.failed.index):
                     <a href="${ url(app_name + ':watch_query', id=query.id) }?context=${qcontext|u}" data-row-selector="true">${_('Results')}</a>
                   % else:
                     ~

+ 6 - 0
apps/beeswax/src/beeswax/templates/show_tables.mako

@@ -30,12 +30,18 @@ ${layout.menubar(section='tables')}
         <div class="span3">
             <div class="well sidebar-nav">
                 <ul class="nav nav-list">
+                    <span
+                        % if app_name == 'impala':
+                            class="hide"
+                        % endif
+                    >
                     <li class="nav-header">${_('database')}</li>
                     <li>
                        <form action="${ url(app_name + ':show_tables') }" id="db_form" method="POST">
                          ${ db_form | n,unicode }
                        </form>
                     </li>
+                    </span>
                     <li class="nav-header">${_('Actions')}</li>
                     % if not examples_installed:
                     <li><a href="#installSamples" data-toggle="modal">${_('Install samples')}</a></li>

+ 7 - 1
apps/beeswax/src/beeswax/templates/watch_results.mako

@@ -74,6 +74,7 @@ ${layout.menubar(section='query')}
                     %if can_save:
                     <li><a data-toggle="modal" href="#saveAs">${_('Save')}</a></li>
                     % endif
+                    % if app_name != 'impala':
                     <%
                       n_jobs = hadoop_jobs and len(hadoop_jobs) or 0
                       mr_jobs = (n_jobs == 1) and _('MR Job') or _('MR Jobs')
@@ -87,6 +88,7 @@ ${layout.menubar(section='query')}
                         <li class="nav-header">${mr_jobs}</li>
                         <li>${_('No Hadoop jobs were launched in running this query.')}</li>
                     % endif
+                    % endif
                 </ul>
             </div>
 
@@ -229,7 +231,7 @@ ${layout.menubar(section='query')}
     </div>
   </form>
 </div>
-%endif
+%endif.resultTable
 
 
 
@@ -339,8 +341,12 @@ ${layout.menubar(section='query')}
       }
 
       % if app_name == 'impala':
+        % if not download:
           $("#collapse").click();
           $(".sidebar-nav, #expand").hide();
+        % elif not error:
+          $("table").replaceWith("${ _('Download results from the left.') }");
+        % endif
       % endif
     });
 </script>

+ 5 - 1
apps/beeswax/src/beeswax/templates/watch_wait.mako

@@ -31,7 +31,11 @@ ${layout.menubar(section='query')}
 	<h1>${_('Waiting for query...')} ${util.render_query_context(query_context)}</h1>
 	<div class="row-fluid">
 		<div class="span3">
-			<div class="well sidebar-nav">
+			<div class="well sidebar-nav
+			    % if app_name == 'impala':
+			      hive
+			    % endif
+			">
 				<ul class="nav nav-list">
 					<%
 			          n_jobs = hadoop_jobs and len(hadoop_jobs) or 0

+ 27 - 9
apps/beeswax/src/beeswax/views.py

@@ -478,7 +478,8 @@ def execute_query(request, design_id=None):
             return explain_directly(request, query, design, query_server)
           else:
             notify = form.query.cleaned_data.get('email_notify', False)
-            return execute_directly(request, query, query_server, design, on_success_url=on_success_url, notify=notify)
+            download = request.POST.has_key('download')
+            return execute_directly(request, query, query_server, design, on_success_url=on_success_url, notify=notify, download=download)
         except BeeswaxException, ex:
           print ex.errorCode
           print ex.SQLState
@@ -536,6 +537,8 @@ def watch_query(request, id):
 
   # GET param: on_success_url. Default to view_results
   results_url = reverse(get_app_name(request) + ':view_results', kwargs={'id': id, 'first_row': 0})
+  if request.GET.get('download', ''):
+    results_url += '?download=true'
   on_success_url = request.GET.get('on_success_url')
   if not on_success_url:
     on_success_url = results_url
@@ -615,6 +618,9 @@ def view_results(request, id, first_row=0):
   context_param = request.GET.get('context', '')
   query_context = _parse_query_context(context_param)
 
+  # To remove in Hue 2.3
+  download  = request.GET.get('download', '')
+
   # Update the status as expired should not be accessible
   # Impala does not support startover for now
   expired = state == models.QueryHistory.STATE.expired
@@ -624,13 +630,22 @@ def view_results(request, id, first_row=0):
 
   # Retrieve query results
   try:
-    results = db.fetch(handle, start_over, 100)
-    data = list(results.rows()) # Materialize results
+    if not download:
+      results = db.fetch(handle, start_over, 100)
+      data = list(results.rows()) # Materialize results
 
-    # We display the "Download" button only when we know that there are results:
-    downloadable = first_row > 0 or data
+      # We display the "Download" button only when we know that there are results:
+      downloadable = first_row > 0 or data
+    else:
+      downloadable = True
+      data = []
+      results = type('Result', (object,), {
+                    'rows': 0,
+                    'columns': [],
+                    'has_more': False,
+                    'start_row': 0, })
     log = db.get_log(handle)
-  except BeeswaxException, ex:
+  except Exception, ex:
     fetch_error = True
     error_message, log = expand_exception(ex, db)
 
@@ -644,12 +659,13 @@ def view_results(request, id, first_row=0):
     'results': data,
     'expected_first_row': first_row,
     'log': log,
-    'hadoop_jobs': _parse_out_hadoop_jobs(log),
+    'hadoop_jobs': app_name != 'impala' and _parse_out_hadoop_jobs(log),
     'query_context': query_context,
     'can_save': False,
     'context_param': context_param,
     'expired': expired,
-    'app_name': app_name
+    'app_name': app_name,
+    'download': download
   }
 
   if not error:
@@ -670,7 +686,7 @@ def view_results(request, id, first_row=0):
       'columns': results.columns,
       'download_urls': download_urls,
       'save_form': save_form,
-      'can_save': query_history.owner == request.user,
+      'can_save': query_history.owner == request.user and not download,
     })
 
   return render('watch_results.mako', request, context)
@@ -1110,6 +1126,8 @@ def execute_directly(request, query, query_server=None, design=None, tablename=N
 
   history_obj = db.execute_query(query, design)
   watch_url = reverse(get_app_name(request) + ':watch_query', kwargs={'id': history_obj.id})
+  if 'download' in kwargs and kwargs['download']:
+    watch_url += '?download=true'
 
   # Prepare the GET params for the watch_url
   get_dict = QueryDict(None, mutable=True)

+ 6 - 9
apps/oozie/src/oozie/templates/dashboard/list_oozie_workflow.mako

@@ -99,18 +99,15 @@ ${ layout.menubar(section='dashboard') }
                    data-confirmation-message="${ _('Are you sure you\'d like to kill this job?') }">
                 ${_('Kill')}
                 </button>
-              % endif
-              % if oozie_workflow.id:
+              % else:
+                % if oozie_workflow.id:
                   <button title="${ _('Rerun the same workflow') }" id="rerun-btn"
                      data-rerun-url="${ url('oozie:rerun_oozie_job', job_id=oozie_workflow.id, app_path=oozie_workflow.appPath) }"
-                     class="btn btn-small
-                % if oozie_workflow.is_running():
-                hide
+                     class="btn btn-small">
+                    ${ _('Rerun') }
+                  </button>
+                  <div id="rerun-wf-modal" class="modal hide"></div>
                 % endif
-                ">
-                  ${ _('Rerun') }
-                </button>
-                <div id="rerun-wf-modal" class="modal hide"></div>
               % endif
               </li>
           % endif

+ 1 - 1
desktop/libs/liboozie/src/liboozie/conf.py

@@ -67,7 +67,7 @@ def config_validator():
   for cluster in get_all_hdfs().values():
     res.extend(validate_path(REMOTE_DEPLOYMENT_DIR, is_dir=True, fs=cluster,
                              message=_('The deployment directory of Oozie workflows does not exist. '
-                                       'Run "Setup Example" on the Oozie workflow page.')))
+                                       'Run "Setup Examples" on the Oozie workflow page.')))
     res.extend(validate_path(ConfigMock('/user/oozie/share/lib'), is_dir=True, fs=cluster,
                              message=_('Oozie Share Lib not installed in default location.')))