Browse Source

[impala] Save dashboard

Romain Rigaux 11 years ago
parent
commit
0174747f96

+ 31 - 1
apps/impala/src/impala/dashboards.py

@@ -105,20 +105,22 @@ def query(request):
   query_server = get_query_server_config(name='impala')
   db = dbms.get(request.user, query_server=query_server)
   
-  query = hql_query(hql)
   print hql
+  query = hql_query(hql)  
   handle = db.execute_and_wait(query, timeout_sec=35.0)
 
   if handle:
     data = db.fetch(handle, rows=100)
     if 'facet' in request.POST:
       facet = json.loads(request.POST.get('facet'))
+      result['type'] = facet['type']
       if facet['type'] == 'top':
         result['data'] = [{"value": row[0], "count": None, "selected": False, "cat": facet['field']} for row in data.rows()]
       else:
         result['data'] = [{"value": row[0], "count": row[1], "selected": False, "cat": facet['field']} for row in data.rows()]
     else:
       result['data'] = list(data.rows())
+
     result['cols'] = list(data.cols())
     result['status'] = 0
     db.close(handle)
@@ -165,6 +167,34 @@ def new_search(request):
   })
 
 
+def save(request):
+  response = {'status': -1}
+
+  dashboard = json.loads(request.POST.get('dashboard', '{}')) # TODO perms
+  layout = json.loads(request.POST.get('layout', '{}'))
+
+  collection['template']['extracode'] = escape(collection['template']['extracode'])
+
+  if collection:
+    if collection['id']:
+      hue_collection = Collection.objects.get(id=collection['id'])
+    else:
+      hue_collection = Collection.objects.create2(name=collection['name'], label=collection['label'])
+    hue_collection.update_properties({'collection': collection})
+    hue_collection.update_properties({'layout': layout})
+    hue_collection.name = collection['name']
+    hue_collection.label = collection['label']
+    hue_collection.enabled = collection['enabled']
+    hue_collection.save()
+    response['status'] = 0
+    response['id'] = hue_collection.id
+    response['message'] = _('Page saved !')
+  else:
+    response['message'] = _('There is no collection to search.')
+
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+
 def get_fields(request):
   result = {'status': -1, 'message': 'Error'}
 

+ 31 - 8
apps/impala/src/impala/templates/dashboard.mako

@@ -90,6 +90,7 @@ ${ commonheader(None, "impala", user) | n,unicode }
     % if user.is_superuser:
       <a title="${ _('Edit') }" rel="tooltip" data-placement="bottom" data-bind="click: toggleEditing, css: {'btn': true, 'btn-inverse': isEditing}"><i class="fa fa-pencil"></i></a>
       &nbsp;&nbsp;&nbsp;
+      <button type="button" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }" data-bind="click: $root.save, css: {'btn': true}"><i class="fa fa-save"></i></button>
       <a class="btn" href="${ url('impala:new_search') }" title="${ _('New') }" rel="tooltip" data-placement="bottom" data-bind="css: {'btn': true}"><i class="fa fa-file-o"></i></a>      
     % endif
   </div>
@@ -221,15 +222,37 @@ ${ dashboard.layout_skeleton() }
       <input type="text" data-bind="value: $data.type" />
     </div>
   
-    <span data-bind="foreach: {data: data(), afterRender: function(){ $root.getWidgetById($parent.id()).isLoading(false); }} ">
-      <div>
-        <a href="javascript: void(0)">
-          <span data-bind="text: $data.value, click: function(){ $root.query.toggleFacet({facet: $data, widget: $parent}) }"></span>
-          (<span data-bind="text: $data.count, click: function(){ $root.query.toggleFacet({facet: $data, widget: $parent}) }"></span>)
-          <i class="fa fa-times" data-bind="visible: $parent.data().length == 1"></i>
-        </a>
+    <!-- ko if: type() == 'field' -->
+      <span data-bind="foreach: {data: data(), afterRender: function(){ $root.getWidgetById($parent.id()).isLoading(false); }}">
+        <div>
+          <a href="javascript: void(0)">
+            <span data-bind="text: $data.value, click: function(){ $root.query.toggleFacet({facet: $data, widget: $parent}) }"></span>
+            (<span data-bind="text: $data.count, click: function(){ $root.query.toggleFacet({facet: $data, widget: $parent}) }"></span>)
+            <i class="fa fa-times" data-bind="visible: $parent.data().length == 1"></i>
+          </a>
+        </div>
+      </span>
+    <!-- /ko --> 
+    
+    <!-- ko if: type() == 'range' -->
+      <div data-bind="foreach: {data: data(), afterRender: function(){ $root.getWidgetById($parent.id()).isLoading(false); }}">
+        <div class="trigger-exclude">
+          <!-- ko if: ! selected -->
+            <a class="pointer" data-bind="text: $data.from + ' - ' + $data.to, click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field}) }"></a>
+            <span class="pointer counter" data-bind="text: ' (' + $data.value + ')', click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field}) }"></span>
+            <a class="exclude pointer" data-bind="click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field, 'exclude': true}) }" title="${ _('Exclude this value') }"><i class="fa fa-minus"></i></a>
+          <!-- /ko -->
+          <!-- ko if: selected -->
+            <span class="pointer" data-bind="click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field}) }">
+              <strong data-bind="text: $data.from + ' - ' + $data.to"></strong>
+              <a class="pointer" data-bind="visible: ! exclude"><i class="fa fa-times"></i></a>
+              <a class="pointer" data-bind="visible: exclude"><i class="fa fa-plus"></i></a>
+            </span>
+          <!-- /ko -->
+        </div>
       </div>
-    </span>
+    <!-- /ko -->    
+    
   </div>
   <!-- /ko -->
   

+ 1 - 0
apps/impala/src/impala/urls.py

@@ -30,6 +30,7 @@ urlpatterns += patterns('impala.dashboards',
   
   url(r'^dashboard/new_facet$', 'new_facet', name='new_facet'),
   url(r'^dashboard/new_search$', 'new_search', name='new_search'),
+  url(r'^dashboard/save$', 'save', name='save'),
   url(r'^dashboard/get_fields', 'get_fields', name='get_fields'),
 )
 

+ 50 - 0
apps/impala/static/js/impala-dashboard.ko.js

@@ -83,6 +83,36 @@ var Query = function (vm, query) {
     vm.search();
   }  
   
+  self.selectRangeFacet = function (data) {
+    var fq = self.getFacetFilter(data.widget_id);
+
+    if (fq == null) {
+      self.fqs.push(ko.mapping.fromJS({
+          'id': data.widget_id,
+          'field': data.cat,
+          'filter': [{'value': data.from, 'exclude': false}],
+          'properties': [{'from': data.from, 'to': data.to}],
+          'type': 'range'
+      }));
+    } else {
+      var f = $.grep(fq.filter(), function(f) { return f.value() == data.from; });
+      if (f.length > 0) { // Unselect
+        fq.filter.remove(f[0]);
+        $.each(fq.properties(), function (index, prop) {
+          if (prop && prop.from() == data.from) {
+            fq.properties.remove(prop);
+          }
+        });
+        if (fq.filter().length == 0) {
+          self.removeFilter(ko.mapping.fromJS({'id': data.widget_id}));
+        }
+      } else {
+       fq.filter.push(ko.mapping.fromJS({'exclude': data.exclude ? true : false, 'value': data.from}));
+       fq.properties.push(ko.mapping.fromJS({'from': data.from, 'to': data.to}));
+      }
+    }
+  };  
+  
   self.getFacetFilter = function (facet_id) {
     var _facet = null;
     $.each(self.fqs(), function (index, facet) {
@@ -315,6 +345,26 @@ var ImpalaDashboardViewModel = function (query_json, dashboard_json, initial_jso
       return _widget;
     }
     
+    self.save = function () {
+      $.post("/impala/dashboard/save", {
+          "dashboard": ko.mapping.toJSON(self.dashboard),
+          "layout": ko.mapping.toJSON(self.columns)
+      }, function (data) {
+        if (data.status == 0) {
+          self.collection.id = data.id;
+          $(document).trigger("info", data.message);
+          if (window.location.search.indexOf("dashboard") == -1) {
+            window.location.hash = '#dashboard=' + data.id;
+          }
+        }
+        else {
+          $(document).trigger("error", data.message);
+        }
+      }).fail(function (xhr, textStatus, errorThrown) {
+        $(document).trigger("error", xhr.responseText);
+      });
+    };
+    
     function bareWidgetBuilder(name, type){
       return new Widget({
         size: 12,