Jelajahi Sumber

[oozie] Improve datasets UI

Fixed tests
Added 'delete' buttons
Added initial state handling along with deletion
abec 13 tahun lalu
induk
melakukan
a17c897

+ 14 - 2
apps/oozie/src/oozie/templates/editor/create_coordinator_data.mako

@@ -27,7 +27,7 @@ from django.utils.safestring import mark_safe
 
 <%def name="print_datasets(label, element, formset, direction, show_headers=True)">
   <div id="${element}">
-    <table class="table-condensed dataTable">
+    <table class="table-condensed dataTable" data-missing="#${element}_missing">
       % if show_headers:
       <thead
         % if not formset.forms:
@@ -59,7 +59,7 @@ from django.utils.safestring import mark_safe
   </div>
 
   <style>
-    .designTable th {
+    .dataTable th {
       text-align:left;
     }
   </style>
@@ -71,6 +71,7 @@ from django.utils.safestring import mark_safe
       var datasetHTML = '${ str(formset.empty_form["dataset"]).replace("\r", "").replace("\n", "").replace("\s", "") }';
       var count = initial.length;
       var root = $('#${element}');
+      var table = root.find('table');
       var totalFormsEl = root.find('input[type=hidden]').filter(function() {
         return this.name.indexOf('TOTAL_FORMS') != -1;
       });
@@ -78,16 +79,25 @@ from django.utils.safestring import mark_safe
 
       window.viewModel.${element} = ko.observableArray(initial);
 
+      $(table.attr('data-missing')).trigger('register', function() {
+        return window.viewModel.${element}().length == 0;
+      });
+
       window.viewModel.add_${element} = function() {
         var newNameHTML = nameHTML.replace(new RegExp("__prefix__", 'g'), count);
         var newDatasetHTML = datasetHTML.replace(new RegExp("__prefix__", 'g'), count++);
         window.viewModel.${element}.push({name: newNameHTML, dataset: newDatasetHTML, error_message: ""});
+
         totalFormsEl.val(window.viewModel.${element}().length);
+
+        $(table.attr('data-missing')).trigger('initOff', table);
       };
 
       window.viewModel.remove_${element} = function(val) {
         window.viewModel.${element}.remove(val);
 
+        // The following logic is to manage formsets dynamically
+        // Replace __prefix__ with '0', '1', '2' and update management table of formset
         var els = root.find(':input').filter(function() {
           return !!$(this).attr('name') && $(this).attr('name').search(/\-\d+\-/) != -1;
         });
@@ -104,6 +114,8 @@ from django.utils.safestring import mark_safe
         }
 
         totalFormsEl.val(window.viewModel.${element}().length);
+
+        $(table.attr('data-missing')).trigger('reinit', table);
       };
     });
   </script>

+ 107 - 23
apps/oozie/src/oozie/templates/editor/edit_coordinator.mako

@@ -132,7 +132,7 @@ ${ layout.menubar(section='coordinators') }
               <h3>${ _('Inputs') }</h3>
 
               % if data_input_formset.forms:
-                <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
+                <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0" data-missing="#dataset_input_missing">
                   <thead>
                     <tr>
                       <th width="10%">${ _('Name') }</th>
@@ -151,18 +151,21 @@ ${ layout.menubar(section='coordinators') }
                          <td>${ form['dataset'] }</td>
                          <td>${ form['dataset'].form.instance.dataset.uri }</td>
                          % if can_edit_coordinator:
-                           <td>${ form['DELETE'] }</td>
+                           <td><a class="btn btn-small delete-row" href="javascript:void(0);">${ _('Delete') }${ form['DELETE'] }</a></td>
                          % endif
                       </tr>
                     % endfor
                   </tbody>
                 </table>
-              % else:
-                <br/>
-                <div class="alert alert-error">
-                  ${ _('No inputs') }
-                </div>
               % endif
+              <br/>
+              <div id="dataset_input_missing" data-missing-bind="true" class="alert alert-error
+                % if data_input_formset.forms:
+                  hide
+                % endif
+              ">
+                ${ _('No inputs') }
+              </div>
 
               % if can_edit_coordinator:
                 ${ coordinator_data.print_datasets(_('Datasets'), 'dataset_input', new_data_input_formset, 'input', not len(data_input_formset.forms)) }
@@ -176,7 +179,7 @@ ${ layout.menubar(section='coordinators') }
             <div class="row-fluid">
               <h3>${ _('Outputs') }</h3>
               % if data_output_formset.forms:
-              <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
+              <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0" data-missing="#dataset_output_missing">
                 <thead>
                   <tr>
                     <th width="10%">${ _('Name') }</th>
@@ -195,18 +198,21 @@ ${ layout.menubar(section='coordinators') }
                       <td>${ form['dataset'] }</td>
                       <td>${ form['dataset'].form.instance.dataset.uri }</td>
                       % if can_edit_coordinator:
-                        <td>${ form['DELETE'] }</td>
+                        <td><a class="btn btn-small delete-row" href="javascript:void(0);">${ _('Delete') }${ form['DELETE'] }</a></td>
                       % endif
                     </tr>
                   % endfor
                 </tbody>
               </table>
-              % else:
-                <br/>
-                <div class="alert alert-error">
-                  ${ _('No outputs') }
-                </div>
               % endif
+              <br/>
+              <div id="dataset_output_missing" data-missing-bind="true" class="alert alert-error
+                % if data_output_formset.forms:
+                  hide
+                % endif
+              ">
+                ${ _('No outputs') }
+              </div>
 
               % if can_edit_coordinator:
                 ${ coordinator_data.print_datasets(_('Datasets'), 'dataset_output', new_data_output_formset, 'output', not len(data_output_formset.forms)) }
@@ -221,7 +227,7 @@ ${ layout.menubar(section='coordinators') }
       <div class="row-fluid">
           <div class="span1">
             % if can_edit_coordinator:
-              <table>
+              <table cellpadding="5">
                 <thead>
                   <tr>
                     <th>${ _('Add a new dataset') }</th>
@@ -229,7 +235,10 @@ ${ layout.menubar(section='coordinators') }
                 </thead>
                 <tbody>
                   <tr>
-                    <td><br/><a class="btn" data-toggle="modal" href="#add-dataset-modal">${ _('Create') }</a></td>
+                    <td class="alert-error"><b>Warning</b>: Save your coordinator before creating a new dataset!</td>
+                  </tr>
+                  <tr>
+                    <td><a class="btn" data-toggle="modal" href="#add-dataset-modal">${ _('Create') }</a></td>
                   </tr>
                 </tbody>
               </table>
@@ -239,7 +248,7 @@ ${ layout.menubar(section='coordinators') }
           <div class="span9">
             % if coordinator.id:
               <div>
-                <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
+                <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0" data-missing="#dataset_missing">
                   <thead>
                     <tr>
                       <th>${ _('Name') }</th>
@@ -274,7 +283,9 @@ ${ layout.menubar(section='coordinators') }
                       <td>${ form.instance.timezone }</td>
                       <td>${ form.instance.done_flag }</td>
                       % if can_edit_coordinator:
-                        <td>${ form['DELETE'] }</td>
+                        <td data-row-selector-exclude="true">
+                          <a class="btn btn-small delete-row" href="javascript:void(0);">${ _('Delete') }${ form['DELETE'] }</a>
+                        </td>
                       % endif
                     </tr>
 
@@ -289,11 +300,13 @@ ${ layout.menubar(section='coordinators') }
                   </tbody>
                 </table>
               </div>
-              % if not dataset_formset.forms:
-                <div class="alert alert-error">
-                  ${ _('No datasets') }
-                </div>
-              % endif
+              <div id="dataset_missing" data-missing-bind="true" class="alert alert-error
+                % if dataset_formset.forms:
+                  hide
+                % endif
+              ">
+                ${ _('No datasets') }
+              </div>
             % endif
           </div>
        </div>
@@ -364,6 +377,12 @@ ${ layout.menubar(section='coordinators') }
   </div>
 </div>
 
+<style type="text/css">
+  .delete-row input {
+    display: none;
+  }
+</style>
+
 <link rel="stylesheet" href="/static/ext/css/jquery-ui-datepicker-1.8.23.css" type="text/css" media="screen" title="no title" charset="utf-8" />
 <link rel="stylesheet" href="/static/ext/css/jquery-timepicker.css" type="text/css" media="screen" title="no title" charset="utf-8" />
 
@@ -395,6 +414,50 @@ ${ layout.menubar(section='coordinators') }
      });
   }
 
+
+  /**
+   * Initial state is used to define when to display the "initial state" of a table.
+   * IE: if there are no formset forms to display, show an "empty" message.
+   *
+   * First, we build a registry of all functions that need to pass in order for us to display the initial state.
+   * Things that 'remove' or 'add' elements will need to trigger 'reinit' and 'initOff' events on their respective 'initial state' elements.
+   * 'Initial state' elements should have 'data-missing-bind="true"' so that custom events can be binded to them.
+   *
+   * args:
+   *  test_func - function that, if true, will indicate that the initial state should be shown.
+   *  hook - If we do show the initial state, run this function before showing it.
+   */
+  var initialStateRegistry = {};
+
+  $("*[data-missing-bind='true']").on('register', function(e, test_func, hook) {
+    var id = $(this).attr('id');
+    if (!initialStateRegistry[id]) {
+      initialStateRegistry[id] = [];
+    }
+    initialStateRegistry[id].push({ test: test_func, hook: hook });
+  });
+
+  $("*[data-missing-bind='true']").on('reinit', function(e) {
+    var show = true;
+    var id = $(this).attr('id');
+    for (var i in initialStateRegistry[id]) {
+      show = show && initialStateRegistry[id][i].test();
+    }
+    if (show) {
+      for (var i in initialStateRegistry[id]) {
+        if (!!initialStateRegistry[id][i].hook) {
+          initialStateRegistry[id][i].hook();
+        }
+      }
+      $(this).show();
+    }
+  });
+
+  $("*[data-missing-bind='true']").on('initOff', function(e) {
+    $(this).hide();
+  });
+
+
   $(document).ready(function() {
     $("input.date").datepicker();
     $("input.time").timePicker(timeOptions);
@@ -416,6 +479,27 @@ ${ layout.menubar(section='coordinators') }
       );
     });
 
+    $('.delete-row').click(function() {
+      var el = $(this);
+      var row = el.closest('tr');
+      var table = el.closest('table');
+      el.find(':input').attr('checked', 'checked');
+      row.hide();
+      $(table.attr('data-missing')).trigger('reinit', table);
+    });
+
+    $('.delete-row').closest('table').each(function() {
+      var table = $(this);
+      var id = table.attr('data-missing');
+      if (!!id) {
+        $( id ).trigger('register', [ function() {
+          return table.find('tbody tr').length == table.find('tbody tr:hidden').length;
+        }, function() {
+          table.hide();
+        } ] );
+      }
+    });
+
     $("a[data-row-selector='true']").jHueRowSelector();
 
     ko.applyBindings(window.viewModel);

+ 1 - 1
apps/oozie/src/oozie/tests.py

@@ -824,7 +824,7 @@ class TestEditor:
     create_dataset(coord)
 
     response = self.c.post(reverse('oozie:create_coordinator_data', args=[coord.id, 'input']),
-                           {u'name': [u'input_dir'], u'dataset': [u'1']})
+                           {u'input-name': [u'input_dir'], u'input-dataset': [u'1']})
     data = json.loads(response.content)
     assert_equal(0, data['status'], data['data'])
 

+ 1 - 1
desktop/core/ext-py/Django-1.2.3/django/utils/autoreload.py

@@ -51,7 +51,7 @@ _win = (sys.platform == "win32")
 def code_changed():
     global _mtimes, _win
     for filename in filter(lambda v: v, map(lambda m: getattr(m, "__file__", None), sys.modules.values())):
-        if filename.endswith(".pyc") or filename.endswith(".pyo") or filename.endswith(".mako"):
+        if filename.endswith(".pyc") or filename.endswith(".pyo"):
             filename = filename[:-1]
         if not os.path.exists(filename):
             continue # File might be in an egg, so it can't be reloaded.