ソースを参照

HUE-417. Beeswax views with big white bar at the bottom

This was a change with how SplitView calculates its target size. Previously we always filled the window, but to allow SplitView to nest for multi-split layouts we had to switch to measuring the parent element of the splitview element. This introduced this sizing problem. The new requirement is that the splitview has to be the root element or, if not, the parent of the splitview needs to have a SizeTo filter to configure its height. This happens in our code often when we have a div.view in the root. The simple fix, when possible, is to also make this div the splitview element. I've adjusted this in all our current usages.
Aaron Newton 15 年 前
コミット
81b61d6ccb

+ 112 - 110
apps/beeswax/src/beeswax/templates/describe_table.mako

@@ -16,122 +16,124 @@
 <%namespace name="wrappers" file="header_footer.mako" />
 <%namespace name="wrappers" file="header_footer.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 ${wrappers.head("Beeswax Table Metadata: " + table.tableName, section='tables')}
 ${wrappers.head("Beeswax Table Metadata: " + table.tableName, section='tables')}
-<div id="describe_table" class="view">
-  <%def name="column_table(cols)">
-    <div class="bw-col_table_wrapper">
-      <table class="sortable" data-filters="HtmlTable" cellpadding="0" cellspacing="0">
-        <thead>
+<%def name="column_table(cols)">
+  <div class="bw-col_table_wrapper">
+    <table class="sortable" data-filters="HtmlTable" cellpadding="0" cellspacing="0">
+      <thead>
+        <tr>
+          <th>Name</th>
+          <th>Type</th>
+          <th>Comment</th>
+        </tr>
+      </thead>
+      <tbody>
+        % for column in cols:
           <tr>
           <tr>
-            <th>Name</th>
-            <th>Type</th>
-            <th>Comment</th>
+            <td>${ column.name }</td>
+            <td>${ column.type }</td>
+            <td>${ column.comment or "" }</td>
           </tr>
           </tr>
-        </thead>
-        <tbody>
-          % for column in cols:
-            <tr>
-              <td>${ column.name }</td>
-              <td>${ column.type }</td>
-              <td>${ column.comment or "" }</td>
-            </tr>
-          % endfor
-        </tbody>
-      </table>
-    </div>
-  </%def>
+        % endfor
+      </tbody>
+    </table>
+  </div>
+</%def>
 
 
-  <div class="resizable" data-filters="SplitView">
-    <div class="left_col">
-      <h2>${table.tableName}</h2>
-      <div class="jframe_padded">
-        <dl>
-          % if table.parameters.get("comment", False):
-            <dt class="hue-dt_cap">Description</dt>
-            <dd class="hue-dd_bottom">${ table.parameters.get("comment") }</dd>
-          % endif
-          <dt class="hue-dt_cap">Actions</dt>
-          <dd class="hue-dd_bottom bw-actions">
-            <ul>
-              <li class="jframe-clear" data-filters="CollapsingElements"><a class="bw-load_data collapser">Import Data</a>
-                  <div class="collapsible jframe-hidden">
-                    <form action="${ url("beeswax.views.load_table", table=table_name) }">
-                      <dl>
-                        <div class="bw-dataNote">
-                        Note that loading data will move data from its location into the table's storage location.
-                        </div>
-                        ## Any existing data will be erased!
-                        <div class="bw-overwriteLabel">Overwrite existing data ? <input type="checkbox" name="overwrite" class="bw-overwriteCheckbox"/></div>
-                        ##Path (on HDFS) of files to load.
-                        ${comps.field(load_form["path"], title_klass='bw-pathLabel', attrs=dict(
-                          klass='bw-loadPath',
-                          data_filters="OverText",
-                          alt='/user/data'))}
-                        <div class="clearfix" data-filters="ArtButtonBar">
-                          <a class="hue-choose_file" data-filters="ArtButton" 
-                            data-icon-styles="{'width': 16, 'height': 16, 'top': 1, 'left': 4 }" data-chooseFor="path">Choose File</a>
-                          <input type="submit" class="bw-loadSubmit" data-filters="ArtButton" value="Submit"/>
-                        </div>
-                        
-                        % for pf in load_form.partition_columns:
-                          ${comps.field(load_form[pf], render_default=True)}
-                        % endfor
-                        ## This table is partitioned.  Therefore,
-                        ## you must specify what partition
-                        ## this data corresponds to.
-                      </dl>
-                    </form>
-                  </div>
-              </li>
-              <li class="jframe-clear"><a href="${ url("beeswax.views.read_table", table=table_name) }" class="bw-browse_data">Browse Data</a></li>
-              <li class="jframe-clear"><a href="${ url("beeswax.views.drop_table", table=table_name) }" class="bw-drop_table">Drop Table</a></li>
-              <li class="jframe-clear"><a href="${hdfs_link}" target="FileBrowser" class="tip bw-location_link" data-filters="PointyTip" data-tip-direction="11" rel="${ table.sd.location }">View File Location</a></li>
-            </ul>
-          </dd>
-        </dl>
-      </div>
+<div id="describe_table resizable" class="view" data-filters="SplitView">
+  <div class="left_col">
+    <h2>${table.tableName}</h2>
+    <div class="jframe_padded">
+      <dl>
+        % if table.parameters.get("comment", False):
+          <dt class="hue-dt_cap">Description</dt>
+          <dd class="hue-dd_bottom">${ table.parameters.get("comment") }</dd>
+        % endif
+        <dt class="hue-dt_cap">Actions</dt>
+        <dd class="hue-dd_bottom bw-actions">
+          <ul>
+            <li class="jframe-clear" data-filters="CollapsingElements"><a class="bw-load_data collapser">Import Data</a>
+                <div class="collapsible jframe-hidden">
+                  <form action="${ url("beeswax.views.load_table", table=table_name) }">
+                    <dl>
+                      <div class="bw-dataNote">
+                      Note that loading data will move data from its location into the table's storage location.
+                      </div>
+                      ## Any existing data will be erased!
+                      <div class="bw-overwriteLabel">Overwrite existing data ? <input type="checkbox" name="overwrite" class="bw-overwriteCheckbox"/></div>
+                      ##Path (on HDFS) of files to load.
+                      ${comps.field(load_form["path"], title_klass='bw-pathLabel', attrs=dict(
+                        klass='bw-loadPath',
+                        data_filters="OverText",
+                        alt='/user/data'))}
+                      <div class="clearfix" data-filters="ArtButtonBar">
+                        <a class="hue-choose_file" data-filters="ArtButton" 
+                          data-icon-styles="{'width': 16, 'height': 16, 'top': 1, 'left': 4 }" data-chooseFor="path">Choose File</a>
+                        <input type="submit" class="bw-loadSubmit" data-filters="ArtButton" value="Submit"/>
+                      </div>
+                      
+                      % for pf in load_form.partition_columns:
+                        ${comps.field(load_form[pf], render_default=True)}
+                      % endfor
+                      ## This table is partitioned.  Therefore,
+                      ## you must specify what partition
+                      ## this data corresponds to.
+                    </dl>
+                  </form>
+                </div>
+            </li>
+            <li class="jframe-clear"><a href="${ url("beeswax.views.read_table", table=table_name) }" class="bw-browse_data">Browse Data</a></li>
+            <li class="jframe-clear"><a href="${ url("beeswax.views.drop_table", table=table_name) }" class="bw-drop_table">Drop Table</a></li>
+            <li class="jframe-clear"><a href="${hdfs_link}" target="FileBrowser" class="tip bw-location_link" data-filters="PointyTip" data-tip-direction="11" rel="${ table.sd.location }">View File Location</a></li>
+          </ul>
+        </dd>
+      </dl>
     </div>
     </div>
-    <div class="right_col">
-      <div data-filters="Tabs">
-        <ul class="toolbar tabs">
-          % if top_rows is not None:
-            <li><span>Sample</span></li>
-          % endif
-          <li><span>Columns</span></li>
-          % if len(table.partitionKeys) > 0:
-            <li><span>Partition Columns</span></li>
-          % endif
-        </ul>
-        <ul class="tab_sections jframe-clear">
-          % if top_rows is not None:
-            <li class="bw-table_sample">
-              <table data-filters="HtmlTable" cellpadding="0" cellspacing="0">
-                <thead>
+  </div>
+  <div class="right_col">
+    <div data-filters="Tabs">
+      <ul class="toolbar tabs">
+        % if top_rows is not None:
+          <li><span>Sample</span></li>
+        % endif
+        <li><span>Columns</span></li>
+        % if len(table.partitionKeys) > 0:
+          <li><span>Partition Columns</span></li>
+        % endif
+      </ul>
+      <ul class="tab_sections jframe-clear">
+        % if top_rows is not None:
+          <li class="bw-table_sample">
+            <table data-filters="HtmlTable" cellpadding="0" cellspacing="0">
+              <thead>
+                <tr>
+                  % for col in table.sd.cols:
+                    <th>${col.name}</th>
+                  % endfor
+                </tr>
+              </thead>
+              <tbody>
+                % for i, row in enumerate(top_rows):
                   <tr>
                   <tr>
-                    % for col in table.sd.cols:
-                      <th>${col.name}</th>
+                    % for item in row:
+                      <td>${ item }</td>
                     % endfor
                     % endfor
                   </tr>
                   </tr>
-                </thead>
-                <tbody>
-                  % for i, row in enumerate(top_rows):
-                    <tr>
-                      % for item in row:
-                        <td>${ item }</td>
-                      % endfor
-                    </tr>
-                  % endfor
-                </tbody>
-              </table>
-            </li>
-          % endif
-          <li>${column_table(table.sd.cols)}</li>
-          % if len(table.partitionKeys) > 0:
-            <li>
-              ${column_table(table.partitionKeys)}
-              <a href="${ url("beeswax.views.describe_partitions", table=table_name) }">Show Partitions</a>
-            </li>
-          % endif
-        </ul>
-      </div>
+                % endfor
+              </tbody>
+            </table>
+          </li>
+        % endif
+        <li>${column_table(table.sd.cols)}</li>
+        % if len(table.partitionKeys) > 0:
+          <li>
+            ${column_table(table.partitionKeys)}
+            <a href="${ url("beeswax.views.describe_partitions", table=table_name) }">Show Partitions</a>
+          </li>
+        % endif
+      </ul>
+    </div>
+  </div>
+</div>
+  
 
 
 ${wrappers.foot()}
 ${wrappers.foot()}

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

@@ -92,7 +92,7 @@ ${wrappers.head('Hive Query', section='query')}
 % endif
 % endif
 
 
 <div class="view" id="execute">
 <div class="view" id="execute">
-  <form action="${action}" method="POST" data-filters="FormValidator">
+  <form action="${action}" method="POST" data-filters="FormValidator, SizeTo" data-size-to-height="0" style="overflow:hidden">
     <div class="resizable" data-filters="SplitView">
     <div class="resizable" data-filters="SplitView">
       <%
       <%
         if form.settings.forms or form.file_resources.forms or form.functions.forms:
         if form.settings.forms or form.file_resources.forms or form.functions.forms:

+ 18 - 20
apps/beeswax/src/beeswax/templates/explain.mako

@@ -17,27 +17,25 @@
 <%namespace name="wrappers" file="header_footer.mako" />
 <%namespace name="wrappers" file="header_footer.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
 ${wrappers.head('Query Explanation', section='saved queries')}
 ${wrappers.head('Query Explanation', section='saved queries')}
-<div class="view" id="watch_wait">
-  <div class="resizable" data-filters="SplitView">
-    <div class="left_col">
-      ${util.render_query_context(query_context)}
-    </div>
-    <div class="right_col jframe_padded">
-      <div data-filters="Tabs">
-        <ul class="toolbar bw-results_tabs tabs jframe-right clearfix">
-          <li><span>Explanation</span></li>
-          <li><span>Query</span></li>
-        </ul>
+<div class="view, resizable" id="watch_wait" data-filters="SplitView">
+  <div class="left_col">
+    ${util.render_query_context(query_context)}
+  </div>
+  <div class="right_col jframe_padded">
+    <div data-filters="Tabs">
+      <ul class="toolbar bw-results_tabs tabs jframe-right clearfix">
+        <li><span>Explanation</span></li>
+        <li><span>Query</span></li>
+      </ul>
 
 
-        <ul class="tab_sections jframe-clear">
-          <li>
-            <pre>${explanation}</pre>
-          </li>
-          <li>
-            <pre>${query}</pre>
-          </li>
-        </ul>
-      </div>
+      <ul class="tab_sections jframe-clear">
+        <li>
+          <pre>${explanation}</pre>
+        </li>
+        <li>
+          <pre>${query}</pre>
+        </li>
+      </ul>
     </div>
     </div>
   </div>
   </div>
 </div>
 </div>

+ 116 - 118
apps/beeswax/src/beeswax/templates/watch_results.mako

@@ -18,132 +18,130 @@
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 ${wrappers.head("Beeswax: Query Results", section='query')}
 ${wrappers.head("Beeswax: Query Results", section='query')}
 
 
-<div class="view" id="watch_results">
-  <div class="resizable" data-filters="SplitView">
-    <div class="left_col">
-      ${util.render_query_context(query_context)}
-        <dl class="jframe_padded">
-          % if download_urls:
-            ## Download results
-            <dt class="hue-dt_cap">Actions</dt>
-            <dd class="hue-dd_bottom bw-actions">
-              <ul>
-                <li><a target="_blank" href="${download_urls["csv"]}" class="bw-download_csv">Download as CSV</a>
-                <li><a target="_blank" href="${download_urls["xls"]}" class="bw-download_xls">Download as XLS</a>
-                <li data-filters="CollapsingElements"><a class="bw-save collapser jframe_ignore" href="${url('beeswax.views.save_results', query.id)}">Save</a>
-                  <div class="collapsible jframe-hidden bw-save_query_results" style="display:none" data-filters="Accordion"> 
-                    <form action="${url('beeswax.views.save_results', query.id) }" method="POST">
-                      ## Writing the save_target fields myself so I can match them to their respective text input fields.
-                      <div> 
-                        <input id="id_save_target_0" type="radio" name="save_target" value="to a new table" class="toggle" checked="checked"/>
-                        <label for="id_save_target_0">In a new table</label>
-                      </div>
-                      ${comps.field(save_form['target_table'], notitle=True, klass="target", attrs=dict(
-                        data_filters="OverText",
-                        alt="table_name"
-                      ))}
-                      <div>
-                        <input id="id_save_target_1" type="radio" name="save_target" value="to HDFS directory" class="toggle"> 
-                        <label for="id_save_target_1">In an HDFS directory</label>
-                      </div>
-                      <div class="target">
-                        ${comps.field(save_form['target_dir'], notitle=True, attrs=dict(
-                        data_filters="OverText",
-                        alt="/user/dir"
-                        ))}
-                        <a data-filters="ArtButton" class="hue-choose_file" data-icon-styles="{'width': 16, 'height': 16, 'top': 1, 'left': 4 }" data-chooseFor="target_dir">Choose File</a>
-                      </div>
-                      <input type="submit" value="Save" name="save" data-filters="ArtButton"> 
-                    </form>
-                  </div>
-                </li>
-              </ul>
-            </dd>
-          % endif
-          <%
-            n_jobs = hadoop_jobs and len(hadoop_jobs) or 0
-            mr_jobs = (n_jobs == 1) and "MR Job" or "MR Jobs"
-          %>
-          <dt class="hue-dt_cap">${mr_jobs}</dt>
+<div class="view resizable" id="watch_results" data-filters="SplitView">
+  <div class="left_col">
+    ${util.render_query_context(query_context)}
+      <dl class="jframe_padded">
+        % if download_urls:
+          ## Download results
+          <dt class="hue-dt_cap">Actions</dt>
           <dd class="hue-dd_bottom bw-actions">
           <dd class="hue-dd_bottom bw-actions">
             <ul>
             <ul>
-              % if n_jobs > 0:
-                <h3 class="jframe-hidden">This query launched ${n_jobs} ${mr_jobs}:</h3>
-                <ul class="beeswax_hadoop_job_links">
-                  % for jobid in hadoop_jobs:
-                  <li><a href="${url("jobbrowser.views.single_job", jobid=jobid)}" target="JobBrowser" class="bw-hadoop_job">${jobid.replace("job_", "")}</a></li>
-                  % endfor
-                </ul>
-              % else:
-                <p class="bw-no_jobs">No Hadoop jobs were launched in running this query.</p>
-              % endif 
+              <li><a target="_blank" href="${download_urls["csv"]}" class="bw-download_csv">Download as CSV</a>
+              <li><a target="_blank" href="${download_urls["xls"]}" class="bw-download_xls">Download as XLS</a>
+              <li data-filters="CollapsingElements"><a class="bw-save collapser jframe_ignore" href="${url('beeswax.views.save_results', query.id)}">Save</a>
+                <div class="collapsible jframe-hidden bw-save_query_results" style="display:none" data-filters="Accordion"> 
+                  <form action="${url('beeswax.views.save_results', query.id) }" method="POST">
+                    ## Writing the save_target fields myself so I can match them to their respective text input fields.
+                    <div> 
+                      <input id="id_save_target_0" type="radio" name="save_target" value="to a new table" class="toggle" checked="checked"/>
+                      <label for="id_save_target_0">In a new table</label>
+                    </div>
+                    ${comps.field(save_form['target_table'], notitle=True, klass="target", attrs=dict(
+                      data_filters="OverText",
+                      alt="table_name"
+                    ))}
+                    <div>
+                      <input id="id_save_target_1" type="radio" name="save_target" value="to HDFS directory" class="toggle"> 
+                      <label for="id_save_target_1">In an HDFS directory</label>
+                    </div>
+                    <div class="target">
+                      ${comps.field(save_form['target_dir'], notitle=True, attrs=dict(
+                      data_filters="OverText",
+                      alt="/user/dir"
+                      ))}
+                      <a data-filters="ArtButton" class="hue-choose_file" data-icon-styles="{'width': 16, 'height': 16, 'top': 1, 'left': 4 }" data-chooseFor="target_dir">Choose File</a>
+                    </div>
+                    <input type="submit" value="Save" name="save" data-filters="ArtButton"> 
+                  </form>
+                </div>
+              </li>
             </ul>
             </ul>
           </dd>
           </dd>
-        </dl>
-    </div>
-    <div class="right_col">
-      <div data-filters="Tabs">
-          <ul class="toolbar bw-results_tabs tabs jframe-right clearfix">
-            <li><span>
-                % if error:
-                  Error
-                % else:
-                  Results
-                % endif
-              </span></li>
-            <li><span>Query</span></li>
-            <li><span>Log</span></li>
+        % endif
+        <%
+          n_jobs = hadoop_jobs and len(hadoop_jobs) or 0
+          mr_jobs = (n_jobs == 1) and "MR Job" or "MR Jobs"
+        %>
+        <dt class="hue-dt_cap">${mr_jobs}</dt>
+        <dd class="hue-dd_bottom bw-actions">
+          <ul>
+            % if n_jobs > 0:
+              <h3 class="jframe-hidden">This query launched ${n_jobs} ${mr_jobs}:</h3>
+              <ul class="beeswax_hadoop_job_links">
+                % for jobid in hadoop_jobs:
+                <li><a href="${url("jobbrowser.views.single_job", jobid=jobid)}" target="JobBrowser" class="bw-hadoop_job">${jobid.replace("job_", "")}</a></li>
+                % endfor
+              </ul>
+            % else:
+              <p class="bw-no_jobs">No Hadoop jobs were launched in running this query.</p>
+            % endif 
           </ul>
           </ul>
+        </dd>
+      </dl>
+  </div>
+  <div class="right_col">
+    <div data-filters="Tabs">
+        <ul class="toolbar bw-results_tabs tabs jframe-right clearfix">
+          <li><span>
+              % if error:
+                Error
+              % else:
+                Results
+              % endif
+            </span></li>
+          <li><span>Query</span></li>
+          <li><span>Log</span></li>
+        </ul>
 
 
-        <ul class="tab_sections jframe-clear">
-          <li>
-            % if error:
-              <div class="jframe-error jframe_padded">
-                <h3 class="jframe-hidden">Error!</h3> 
-                <pre>${error_message}</pre>
-              </div>
-            % else:
-              <div class="bw-result_nav toolbar">
-                % if has_more:
-                  <a href="${ url('beeswax.views.view_results', query.id, next_row) }" title="Next page" class="bw-nextBlock">[next]</a>
-                % endif
-                % if start_row != 0:
-                  <a href="${ url('beeswax.views.view_results', query.id, 0) }" title="Back to first row" class="bw-firstBlock">[top]</a>
-                % endif
-              </div>
-              % if expected_first_row != start_row:
-                <div class="bw-result_warning">Warning:</i> Page offset may have incremented since last view.</div>
+      <ul class="tab_sections jframe-clear">
+        <li>
+          % if error:
+            <div class="jframe-error jframe_padded">
+              <h3 class="jframe-hidden">Error!</h3> 
+              <pre>${error_message}</pre>
+            </div>
+          % else:
+            <div class="bw-result_nav toolbar">
+              % if has_more:
+                <a href="${ url('beeswax.views.view_results', query.id, next_row) }" title="Next page" class="bw-nextBlock">[next]</a>
               % endif
               % endif
-              <table data-filters="HtmlTable" cellpadding="0" cellspacing="0">
-                <thead>
-                  <tr>
-                    <th>-</th>
-                    % for col in columns:
-                      <th>${col}</th>
-                    % endfor
-                  </tr>
-                </thead>
-                <tbody>
-                  % for i, row in enumerate(results):
-                  <tr>
-                    <td>${start_row + i}</td>
-                    % for item in row:
-                      <td>${ item }</td>
-                    % endfor
-                  </tr>
-                  % endfor
-                </tbody>
-              </table>
+              % if start_row != 0:
+                <a href="${ url('beeswax.views.view_results', query.id, 0) }" title="Back to first row" class="bw-firstBlock">[top]</a>
+              % endif
+            </div>
+            % if expected_first_row != start_row:
+              <div class="bw-result_warning">Warning:</i> Page offset may have incremented since last view.</div>
             % endif
             % endif
-          </li>
-          <li class="jframe_padded">
-            <pre>${query.query}</pre>
-          </li>
-          <li class="jframe_padded">
-            <pre>${log}</pre>
-          </li>
-        </ul>
-      </div>
+            <table data-filters="HtmlTable" cellpadding="0" cellspacing="0">
+              <thead>
+                <tr>
+                  <th>-</th>
+                  % for col in columns:
+                    <th>${col}</th>
+                  % endfor
+                </tr>
+              </thead>
+              <tbody>
+                % for i, row in enumerate(results):
+                <tr>
+                  <td>${start_row + i}</td>
+                  % for item in row:
+                    <td>${ item }</td>
+                  % endfor
+                </tr>
+                % endfor
+              </tbody>
+            </table>
+          % endif
+        </li>
+        <li class="jframe_padded">
+          <pre>${query.query}</pre>
+        </li>
+        <li class="jframe_padded">
+          <pre>${log}</pre>
+        </li>
+      </ul>
     </div>
     </div>
   </div>
   </div>
 </div>
 </div>

+ 48 - 50
apps/beeswax/src/beeswax/templates/watch_wait.mako

@@ -19,58 +19,56 @@ ${wrappers.head("Beeswax: Waiting for query...", section='query')}
 
 
 <meta http-equiv="refresh" content="3;${url('beeswax.views.watch_query', query.id)}?${fwd_params}" />
 <meta http-equiv="refresh" content="3;${url('beeswax.views.watch_query', query.id)}?${fwd_params}" />
 
 
-<div class="view" id="watch_wait">
-  <div class="resizable" data-filters="SplitView">
-    <div class="left_col">
-      ${util.render_query_context(query_context)}
-      <dl class="jframe_padded">
-        % if download_urls:
-        <dt class="hue-dt_cap">Downloads</dt>
-        <dd class="hue-dd_bottom bw-actions">
-          <ul>
-            <li><a target="_blank" href="${download_urls["csv"]}" class="bw-download_csv">Download as CSV</a>
-            <li><a target="_blank" href="${download_urls["xls"]}" class="bw-download_xls">Download as XLS</a>
-          </ul>
-        </dd>
-        % endif
-      <%
-        n_jobs = hadoop_jobs and len(hadoop_jobs) or 0
-        mr_jobs = (n_jobs == 1) and "MR Job" or "MR Jobs"
-      %>
-      <dt class="hue-dt_cap">${mr_jobs}</dt>
-        <dd class="hue-dd_bottom bw-actions">
-          <ul data-single-partial-id="num_jobs">
-            % if n_jobs > 0:
-              <h3 class="jframe-hidden">This query launched ${n_jobs} ${mr_jobs}:</h3>
-              <ul class="beeswax_hadoop_job_links">
-                % for jobid in hadoop_jobs:
-                <li><a href="${url("jobbrowser.views.single_job", jobid=jobid)}" target="JobBrowser" class="bw-hadoop_job">${jobid.replace("job_", "")}</a></li>
-                % endfor
-              </ul>
-            % else:
-              <p class="bw-no_jobs">No Hadoop jobs were launched in running this query.</p>
-            % endif 
-          </ul>
-        </dd>
-      </dl>
-    </div>
-    <div class="right_col jframe_padded">
-      <div data-filters="Tabs">
-        <ul class="toolbar bw-results_tabs tabs jframe-right clearfix">
-          <li><span>Log</span></li>
-          <li><span>Query</span></li>
+<div class="view resizable" id="watch_wait" data-filters="SplitView">
+  <div class="left_col">
+    ${util.render_query_context(query_context)}
+    <dl class="jframe_padded">
+      % if download_urls:
+      <dt class="hue-dt_cap">Downloads</dt>
+      <dd class="hue-dd_bottom bw-actions">
+        <ul>
+          <li><a target="_blank" href="${download_urls["csv"]}" class="bw-download_csv">Download as CSV</a>
+          <li><a target="_blank" href="${download_urls["xls"]}" class="bw-download_xls">Download as XLS</a>
         </ul>
         </ul>
-
-        <ul class="tab_sections jframe-clear">
-          <li>
-            <h3 class="jframe-hidden">Server Log</h3>
-            <pre data-single-partial-id="log">${log}</pre>
-          </li>
-          <li>
-            <pre>${query.query}</pre>
-          </li>
+      </dd>
+      % endif
+    <%
+      n_jobs = hadoop_jobs and len(hadoop_jobs) or 0
+      mr_jobs = (n_jobs == 1) and "MR Job" or "MR Jobs"
+    %>
+    <dt class="hue-dt_cap">${mr_jobs}</dt>
+      <dd class="hue-dd_bottom bw-actions">
+        <ul data-single-partial-id="num_jobs">
+          % if n_jobs > 0:
+            <h3 class="jframe-hidden">This query launched ${n_jobs} ${mr_jobs}:</h3>
+            <ul class="beeswax_hadoop_job_links">
+              % for jobid in hadoop_jobs:
+              <li><a href="${url("jobbrowser.views.single_job", jobid=jobid)}" target="JobBrowser" class="bw-hadoop_job">${jobid.replace("job_", "")}</a></li>
+              % endfor
+            </ul>
+          % else:
+            <p class="bw-no_jobs">No Hadoop jobs were launched in running this query.</p>
+          % endif 
         </ul>
         </ul>
-    </div>
+      </dd>
+    </dl>
+  </div>
+  <div class="right_col jframe_padded">
+    <div data-filters="Tabs">
+      <ul class="toolbar bw-results_tabs tabs jframe-right clearfix">
+        <li><span>Log</span></li>
+        <li><span>Query</span></li>
+      </ul>
+
+      <ul class="tab_sections jframe-clear">
+        <li>
+          <h3 class="jframe-hidden">Server Log</h3>
+          <pre data-single-partial-id="log">${log}</pre>
+        </li>
+        <li>
+          <pre>${query.query}</pre>
+        </li>
+      </ul>
   </div>
   </div>
 </div>
 </div>
 ${wrappers.foot()}
 ${wrappers.foot()}

+ 40 - 43
apps/jobsub/src/jobsub/templates/edit.html

@@ -18,58 +18,55 @@ limitations under the License.
 {% endcomment %}
 {% endcomment %}
 {% block title %}Job Design Editor{% endblock %}
 {% block title %}Job Design Editor{% endblock %}
 {% block content %}
 {% block content %}
-	<div id="jobsub_edit" class="view">
-		
-		<div class="jobsub_col_wrapper" data-filters="SplitView">
-			<div class="jobsub_left_col left_col">
-				{% include "types.html" %}
-			</div>
-			<div class="jobsub_right_col right_col clearfix jframe_padded">
-		
-				<h1>Job Design Editor</h1>
+<div id="jobsub_edit" class="view jobsub_col_wrapper" data-filters="SplitView">
+	<div class="jobsub_left_col left_col">
+		{% include "types.html" %}
+	</div>
+	<div class="jobsub_right_col right_col clearfix jframe_padded">
+
+		<h1>Job Design Editor</h1>
 
 
-				{% if message %}
-				<div align="center" class="jframe-error">{{message}}</div>
-				{% endif %}
+		{% if message %}
+		<div align="center" class="jframe-error">{{message}}</div>
+		{% endif %}
 
 
-				<h2>Job Design Editor: {{form.name}} Job</h2>
-				<form method="POST" action="{{ edit_url }}" enctype="multipart/form-data">
+		<h2>Job Design Editor: {{form.name}} Job</h2>
+		<form method="POST" action="{{ edit_url }}" enctype="multipart/form-data">
 
 
-					<div class="jobsub_edit_tip">
-						<p><b>Tip</b>:
-							Job Designs allow you to rudimentarily parameterize your jobs by using variables.
-							At submission time, you'll be prompted to specify the values of any variables 
-							of the form <i>$variable_name</i>.
-							<!--
-							TODO(philip): Put this in if the file chooser is hooked up.
+			<div class="jobsub_edit_tip">
+				<p><b>Tip</b>:
+					Job Designs allow you to rudimentarily parameterize your jobs by using variables.
+					At submission time, you'll be prompted to specify the values of any variables 
+					of the form <i>$variable_name</i>.
+					<!--
+					TODO(philip): Put this in if the file chooser is hooked up.
 
 
-							The following variables are treated specially, prompting a "file chooser"
-							tool.
+					The following variables are treated specially, prompting a "file chooser"
+					tool.
 
 
-							<dl class="jobsub_notes clearfix">
-							<dt><tt>$input</tt></dt><dd>Input for your job.</dd>
-							<dt><tt>$output</tt></dt><dd>Output directory (must not already exist) for your job.</dd>
-							</dl>
-							-->
-					</div>
+					<dl class="jobsub_notes clearfix">
+					<dt><tt>$input</tt></dt><dd>Input for your job.</dd>
+					<dt><tt>$output</tt></dt><dd>Output directory (must not already exist) for your job.</dd>
+					</dl>
+					-->
+			</div>
 
 
 
 
-					<input type="hidden" name="type" value="{{ form.type_str }}"/>
-					<dl class="jobsub_meta clearfix">
-						<dt>{{ metadata_form.name.html_name }}</dt>
-						<dd>{{ metadata_form.name|safe }}</dd>
+			<input type="hidden" name="type" value="{{ form.type_str }}"/>
+			<dl class="jobsub_meta clearfix">
+				<dt>{{ metadata_form.name.html_name }}</dt>
+				<dd>{{ metadata_form.name|safe }}</dd>
 
 
-						<dt>{{ metadata_form.description.html_name }}</dt>
-						<dd>{{ metadata_form.description|safe }}</dd>
+				<dt>{{ metadata_form.description.html_name }}</dt>
+				<dd>{{ metadata_form.description|safe }}</dd>
 
 
-					</dl>
-					{{ form.render_edit }}
-					<div class="jobsub_edit_save">
-							<input type="submit" name="submit" value="Save" class="hue-submit"/>
-							<label><input type="checkbox" name="save_submit"/> Submit upon save.</label>
-					</div>
-				</form>
+			</dl>
+			{{ form.render_edit }}
+			<div class="jobsub_edit_save">
+					<input type="submit" name="submit" value="Save" class="hue-submit"/>
+					<label><input type="checkbox" name="save_submit"/> Submit upon save.</label>
 			</div>
 			</div>
-		</div>
+		</form>
 	</div>
 	</div>
+</div>
 {% endblock %}
 {% endblock %}

+ 49 - 51
apps/jobsub/src/jobsub/templates/list.html

@@ -18,59 +18,57 @@ limitations under the License.
 {% endcomment %}
 {% endcomment %}
 {% block title %}Job Design List{% endblock %}
 {% block title %}Job Design List{% endblock %}
 {% block content %}
 {% block content %}
-<div id="jobsub_joblist" class="view">
+<div id="jobsub_joblist jobsub_col_wrapper" class="view" data-filters="SplitView">
   <h1>Job Designer: Job Designs</h1>
   <h1>Job Designer: Job Designs</h1>
-  <div class="jobsub_col_wrapper" data-filters="SplitView">
-    <div class="jobsub_left_col left_col">
-      {% include "types.html" %}
-    </div>
-    <div class="jobsub_right_col right_col">
-      <div class="jobsub_topsection clearfix">
-        <form class="jobsub_filter" data-filters="SubmitOnChange" action="{% url jobsub.list %}" method="get">
-          <span class="filter_text">Filter By Owner:</span>
-          <input data-filters="ArtInput, OverText" data-art-input-type="search" name="owner" alt="Filter By Owner" value="{{ owner }}"/>
-          <input data-filters="ArtInput, OverText" data-art-input-type="search" name="name" alt="Filter By Name" value="{{ name }}"/>
-          <input class="submit" type="submit"/>
-        </form>
-      </div>
-    
-      <table data-filters="HtmlTable" class="selectable sortable" cellpadding="0" cellspacing="0">
-        <thead>
-          <tr>
-            <th>Owner</th>
-            <th>Name</th>
-            <th colspan="2">Last Modified</th>
-          </tr>
-        </thead>
-        <tbody>
-      {% for jobdesign in jobdesigns %}
-          <tr 
-            {% ifequal saved jobdesign.id %} class="jobsub_saved"{% endifequal %}
-            data-filters="ContextMenu"
-            data-context-menu-actions="[{'events':['contextmenu','click:relay(.item-options)'],'menu':'ul.context-menu'}]"
-            data-dblclick-delegate= "{'dblclick_loads':'a.run'}">
-            <td>
-              {{ jobdesign.owner.username }}
-              <ul class="context-menu">
-                <li><a title="Launch job with this design" class="run frame_tip" href="{{ jobdesign.submit_url }}">Submit to Cluster</a></li>
-                <li><a title="Copy design" class="copy frame_tip" href="{{ jobdesign.clone_url }}">Copy</a></li>
-                <li><a title="Edit this design" class="edit frame_tip" href="{{ jobdesign.edit_url }}">Edit</a></li>
-                <li><a title="Delete this design" alt="Are you sure you want to delete this design?" class="delete frame_tip" href="{{ jobdesign.delete_url }}">Delete</a></li>
-              </ul>
-            </td>
-            <td>{{ jobdesign.name }}</td>
-            <td>
-              <span class="jobsub_actual_date" style="display: none">{{ jobdesign.last_modified|date:"U" }}</span>
-              {{ jobdesign.last_modified|timesince }} ago
-            </td>
-            <td>
-              <a class="item-options">options</a>
-            </td>
-          </tr>
-      {% endfor %}
-        </tbody>
-      </table>
+  <div class="jobsub_left_col left_col">
+    {% include "types.html" %}
+  </div>
+  <div class="jobsub_right_col right_col">
+    <div class="jobsub_topsection clearfix">
+      <form class="jobsub_filter" data-filters="SubmitOnChange" action="{% url jobsub.list %}" method="get">
+        <span class="filter_text">Filter By Owner:</span>
+        <input data-filters="ArtInput, OverText" data-art-input-type="search" name="owner" alt="Filter By Owner" value="{{ owner }}"/>
+        <input data-filters="ArtInput, OverText" data-art-input-type="search" name="name" alt="Filter By Name" value="{{ name }}"/>
+        <input class="submit" type="submit"/>
+      </form>
     </div>
     </div>
+  
+    <table data-filters="HtmlTable" class="selectable sortable" cellpadding="0" cellspacing="0">
+      <thead>
+        <tr>
+          <th>Owner</th>
+          <th>Name</th>
+          <th colspan="2">Last Modified</th>
+        </tr>
+      </thead>
+      <tbody>
+    {% for jobdesign in jobdesigns %}
+        <tr 
+          {% ifequal saved jobdesign.id %} class="jobsub_saved"{% endifequal %}
+          data-filters="ContextMenu"
+          data-context-menu-actions="[{'events':['contextmenu','click:relay(.item-options)'],'menu':'ul.context-menu'}]"
+          data-dblclick-delegate= "{'dblclick_loads':'a.run'}">
+          <td>
+            {{ jobdesign.owner.username }}
+            <ul class="context-menu">
+              <li><a title="Launch job with this design" class="run frame_tip" href="{{ jobdesign.submit_url }}">Submit to Cluster</a></li>
+              <li><a title="Copy design" class="copy frame_tip" href="{{ jobdesign.clone_url }}">Copy</a></li>
+              <li><a title="Edit this design" class="edit frame_tip" href="{{ jobdesign.edit_url }}">Edit</a></li>
+              <li><a title="Delete this design" alt="Are you sure you want to delete this design?" class="delete frame_tip" href="{{ jobdesign.delete_url }}">Delete</a></li>
+            </ul>
+          </td>
+          <td>{{ jobdesign.name }}</td>
+          <td>
+            <span class="jobsub_actual_date" style="display: none">{{ jobdesign.last_modified|date:"U" }}</span>
+            {{ jobdesign.last_modified|timesince }} ago
+          </td>
+          <td>
+            <a class="item-options">options</a>
+          </td>
+        </tr>
+    {% endfor %}
+      </tbody>
+    </table>
   </div>
   </div>
 </div>
 </div>
 {% endblock %}
 {% endblock %}

+ 1 - 1
ext/thirdparty/js/widgets.hash

@@ -1 +1 @@
-fc7f957aeec5726c8314d4fb2ba22ade5af62b6f
+48b5bd3c49a9e01b4fcc38b0d5b22342e92c747d