Explorar o código

[search] Splitting templates of range/field widgets like text and pie

Romain Rigaux %!s(int64=11) %!d(string=hai) anos
pai
achega
0d23d80

+ 0 - 2
apps/search/src/search/api.py

@@ -122,8 +122,6 @@ class SolrApi(object):
 
     for fq in query['fqs']:
       model_facet = [facet for facet in collection['facets'] if facet['id'] == fq['id']][0]
-      print '---'
-      print model_facet
       if model_facet['type'] == 'field':        
         params += (('fq', ' '.join([urllib.unquote(utf_quoter('{!tag=%s}{!field f=%s}%s' % (fq['field'], fq['field'], _filter))) for _filter in fq['filter']])),)
       elif model_facet['type'] == 'range':

+ 33 - 33
apps/search/src/search/models.py

@@ -460,19 +460,21 @@ def augment_solr_response2(response, collection, query, solr_query):
   normalized_facets = []
 
   def pairwise2(cat, selected_values, iterable):
-      pairs = []
-      a, b = itertools.tee(iterable)
-      for element in a:
-        pairs.append({'cat': cat, 'value': element, 'count': next(a), 'selected': element in selected_values})
-      return pairs
-
-  def reversepairs(iterable):
-      pairs = []
-      a, b = itertools.tee(iterable)
-      for element in a:
-        pairs.insert(0, next(a))
-        pairs.insert(0, element)
-      return pairs
+    pairs = []
+    a, b = itertools.tee(iterable)
+    for element in a:
+      pairs.append({'cat': cat, 'value': element, 'count': next(a), 'selected': element in selected_values})
+    return pairs
+
+  def range_pair(cat, selected_values, iterable, end):
+    # counts":["0",17430,"1000",1949,"2000",671,"3000",404,"4000",243,"5000",165],"gap":1000,"start":0,"end":6000}
+    pairs = []
+    a, to = itertools.tee(iterable)
+    next(to, None)
+    for element in a:
+      next(to, None)
+      pairs.append({'field': cat, 'from': element, 'value': next(a), 'to': next(to, end), 'selected': element == selected_values})
+    return pairs
 
   selected_values = dict([((fq['id'], fq['field'], fq['type']), fq['filter']) for fq in query['fqs']])
 
@@ -480,32 +482,30 @@ def augment_solr_response2(response, collection, query, solr_query):
     # [{u'field': u'sun', u'type': u'query', u'id': u'67b43a63-ed22-747b-47e8-b31aad1431ea', u'label': u'sun'}
     for facet in collection['facets']:
       category = facet['type']
-      
+
       if category == 'field' and response['facet_counts']['facet_fields']:
-        for name in response['facet_counts']['facet_fields']: # todo get from the list
-          #selected_field = fq.get(name, '') # todo with multi filter
-          collection_facet = get_facet_field(category, name, collection['facets'])
-          counts = pairwise2(name, selected_values.get((facet['id'], name, category), []), response['facet_counts']['facet_fields'][name])
-          if collection_facet['properties']['sort'] == 'asc':
-            counts.reverse()          
-          facet = {
-            'id': collection_facet['id'],
-            'field': name,
-            'type': category,
-            'label': collection_facet['label'],
-            'counts': counts,
-            # add total result count?
-          }
-          normalized_facets.append(facet)
+        name = facet['field']
+        collection_facet = get_facet_field(category, name, collection['facets'])
+        counts = pairwise2(name, selected_values.get((facet['id'], name, category), []), response['facet_counts']['facet_fields'][name])
+        if collection_facet['properties']['sort'] == 'asc':
+          counts.reverse()          
+        facet = {
+          'id': collection_facet['id'],
+          'field': name,
+          'type': category,
+          'label': collection_facet['label'],
+          'counts': counts,
+          # add total result count?
+        }
+        normalized_facets.append(facet)
       elif category == 'range' and response['facet_counts']['facet_ranges']:
           name = facet['field']
           collection_facet = get_facet_field(category, name, collection['facets'])
           counts = response['facet_counts']['facet_ranges'][name]['counts']
+          end = response['facet_counts']['facet_ranges'][name]['end']
+          counts = range_pair(name, selected_values.get((facet['id'], name, 'range'), {}).get('from', None), counts, end)
           if collection_facet['properties']['sort'] == 'asc':
-            counts = reversepairs(counts) 
-#          if facet['widgetType'] in ('facet-widget', 'pie-widget'):
-#            print selected_values # 'from'
-#            counts = pairwise2(name, selected_values.get((facet['id'], name, 'field'), []), counts)          
+            counts.reverse()
           facet = {
             'id': collection_facet['id'],
             'field': name,

+ 75 - 32
apps/search/src/search/templates/search2.mako

@@ -220,20 +220,39 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
           <i class="fa" data-bind="css: { 'fa-sort-alpha-asc': properties.sort() == 'desc' && type() != 'range', 'fa-sort-alpha-desc': properties.sort() == 'asc' && type() != 'range', 'fa-sort-numeric-desc': properties.sort() == 'desc' && type() == 'range', 'fa-sort-numeric-asc': properties.sort() == 'asc' && type() == 'range' }"></i>
         </a>  
       </span>
-    </div>  
-
-    <div data-bind="foreach: counts">
-      <div>
-        <a href="script:void(0)">
-        <!-- ko if: selected -->
-          <span data-bind="text: value, click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id}) }"></span>
-          <i data-bind="click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id}) }" class="fa fa-times"></i>            
-        <!-- /ko -->
-        <!-- ko if: ! selected -->           
-          <span data-bind="text: value + ' (' + count + ')', click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id}); }"></span>
-        <!-- /ko -->
-        </a>
-      </div>
+    </div>
+    <div data-bind="with: $root.collection.getFacetById($parent.id())">
+	    <!-- ko if: type() != 'range' -->
+        <div data-bind="foreach: $parent.counts">
+          <div>
+            <a href="script:void(0)">
+              <!-- ko if: ! $data.selected -->
+                <span data-bind="text: $data.value + ' (' + $data.count + ')', click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }"></span>                
+              <!-- /ko -->
+              <!-- ko if: $data.selected -->
+                <span data-bind="text: $data.value, click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }"></span>
+                <i class="fa fa-times"></i>
+              <!-- /ko -->
+            </a>
+          </div>
+        </div>
+	    <!-- /ko -->
+	    <!-- ko if: type() == 'range' -->
+        <div data-bind="foreach: $parent.counts">
+          <div>
+            <a href="script:void(0)">
+              <!-- ko if: ! selected --> 
+                <span data-bind="text: $data.from + ' - ' + $data.to + ' (' + $data.value + ')',
+                  click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field}) }"></span>                
+              <!-- /ko -->
+              <!-- ko if: selected -->
+                <span 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}) }"></span>
+                <i class="fa fa-times"></i>
+              <!-- /ko -->
+            </a>
+          </div>
+        </div>
+	    <!-- /ko -->    
     </div>
   </div>
   <!-- /ko -->
@@ -359,7 +378,8 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
     <a href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a href="javascript:void(0)"><i class="fa fa-minus"></i></a>
 
-    <div data-bind="timelineChart: {data: counts, field: field, label: label, transformer: timelineChartDataTransformer, onSelectRange: function(from, to){viewModel.query.selectRangeFacet({from: from, to: to, cat: field})}, onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
+    <div data-bind="timelineChart: {data: counts, field: field, label: label, transformer: timelineChartDataTransformer,
+      onSelectRange: function(from, to){viewModel.query.selectRangeFacet({from: d.obj.from, to: d.obj.to, cat: d.obj.field})}, onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
   </div>
   <!-- /ko -->
 </script>
@@ -387,8 +407,8 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 
     <div data-bind="barChart: {data: {counts: counts, widget_id: $parent.id()}, field: field, label: label, 
       transformer: barChartDataTransformer,
-      onClick: function(d){viewModel.query.selectRangeFacet({count: d.y, widget_id: d.widget_id, from: d.x, to: d.x_end, cat: field})}, 
-      onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}"
+      onClick: function(d){ viewModel.query.selectRangeFacet({count: d.obj.value, widget_id: d.obj.widget_id, from: d.obj.from, to: d.obj.to, cat: d.obj.field}) }, 
+      onComplete: function(){ viewModel.getWidgetById(id).isLoading(false) } }"
     />
   </div>
   <!-- /ko -->
@@ -399,15 +419,22 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
   <!-- /ko -->
 
-
   <!-- ko if: $root.getFacetFromQuery(id) -->
   <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id)">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       ${ _('Field') }: <input type="text" data-bind="value: field" />
     </div>
-
-    <div data-bind="pieChart: {data: {counts: counts, widget_id: $parent.id()}, transformer: pieChartDataTransformer, onClick: function(d){viewModel.query.toggleFacet({facet: d.data.obj, widget_id: d.data.obj.widget_id})}, onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
+    <div data-bind="with: $root.collection.getFacetById($parent.id())">
+      <!-- ko if: type() == 'range' -->
+      <div data-bind="pieChart: {data: {counts: $parent.counts, widget_id: $parent.id}, transformer: rangePieChartDataTransformer,
+         onClick: function(d){ viewModel.query.selectRangeFacet({count: d.data.obj.value, widget_id: d.data.obj.widget_id, from: d.data.obj.from, to: d.data.obj.to, cat: d.data.obj.field}) }, 
+         onComplete: function(){viewModel.getWidgetById($parent.id).isLoading(false)}}" />
+      <!-- /ko -->
+      <!-- ko if: type() != 'range' -->
+      <div data-bind="pieChart: {data: {counts: $parent.counts, widget_id: $parent.id}, transformer: pieChartDataTransformer, onClick: function(d){viewModel.query.toggleFacet({facet: d.data.obj, widget_id: d.data.obj.widget_id})}, onComplete: function(){viewModel.getWidgetById($parent.id).isLoading(false)}}" />
+      <!-- /ko -->
+    </div>    
   </div>
   <!-- /ko -->
   <div class="widget-spinner" data-bind="visible: isLoading()">
@@ -426,7 +453,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 
 <script type="text/html" id="filter-widget">
   <div data-bind="foreach: $root.query.fqs">
-    <span data-bind="text: ko.mapping.toJSON($data), click: viewModel.query.removeFilter"></span>
+    <span data-bind="text: ko.mapping.toJSON($data), click: function(){ viewModel.query.removeFilter($data); viewModel.search() }"></span>
   </div>
   <div class="widget-spinner" data-bind="visible: isLoading()">
     <!--[if !IE]> --><i class="fa fa-spinner fa-spin"></i><!-- <![endif]-->
@@ -649,29 +676,45 @@ function pieChartDataTransformer(data) {
   return _data;
 }
 
+function rangePieChartDataTransformer(data) {
+  var _data = [];
+  $(data.counts).each(function (cnt, item) {
+    item.widget_id = data.widget_id;
+    _data.push({
+      label: item.from,
+      value: item.value,
+      to: item.to,
+      obj: item
+    });
+  });
+  return _data;
+}
+
 function barChartDataTransformer(data) {
   var _data = [];
-  for (var i = 0; i < data.counts.length; i = i + 2) {
+  $(data.counts).each(function (cnt, item) {
+    item.widget_id = data.widget_id;  
     _data.push({
       series: 0,
-      x: data.counts[i],
-      x_end: data.counts[i + 2],
-      y: data.counts[i + 1],
-      widget_id: data.widget_id
+      x: item.from,
+      x_end: item.to,
+      y: item.value,
+      obj: item
     });
-  }
+  });
   return _data;
 }
 
-function timelineChartDataTransformer(data) {
+function timelineChartDataTransformer(data) { 
   var _data = [];
-  for (var i = 0; i < data.length; i = i + 2) {
+  $(data).each(function (cnt, item) {
     _data.push({
       series: 0,
-      x: new Date(moment(data[i]).valueOf()),
-      y: data[i + 1]
+      x: new Date(moment(item.from).valueOf()),
+      y: item.value,
+      obj: item
     });
-  }
+  });
   return _data;
 }
 

+ 27 - 26
apps/search/static/js/search.ko.js

@@ -201,16 +201,9 @@ var Query = function (vm, query) {
 // {'wid': 1, 'filter: [{'from': 1, 'to': 10}], 'type': 'range', 'field': 'created_at'}
 // {'wid': 2, ....}
 // ]  
-  self.toggleFacet = function (data) {alert(ko.mapping.toJSON(data)); 
+  self.toggleFacet = function (data) {//alert(ko.mapping.toJSON(data)); 
 	var fq = self.getFacetFilter(data.widget_id);
-	
-	// Special case when text facet is a range
-	var _facet = vm.collection.getFacetById(data.widget_id);
-	if (_facet.type() == 'range') {
-      self.selectRangeFacet({'widget_id': data.widget_id, 'cat': data.facet.cat, 'from': data.facet.value, 'to': data.facet.value});
-      return;
-	}
-	
+
 	if (fq == null) {
       self.fqs.push(ko.mapping.fromJS({
         'id': data.widget_id,
@@ -237,14 +230,19 @@ var Query = function (vm, query) {
   }  
   
   self.selectRangeFacet = function (data) {
-    self.removeFilter(ko.mapping.fromJS({'id': data.widget_id}));
+	var fq = self.getFacetFilter(data.widget_id);
+	var unselect = fq != null && fq.id() == data.widget_id;
 	
-    self.fqs.push(ko.mapping.fromJS({
-      'id': data.widget_id,
-      'field': data.cat,
-      'filter': {'from': data.from, 'to': data.to},
-      'type': 'range'
-    }));
+	self.removeFilter(ko.mapping.fromJS({'id': data.widget_id}));
+    
+	if (! unselect) {
+      self.fqs.push(ko.mapping.fromJS({
+        'id': data.widget_id,
+        'field': data.cat,
+        'filter': {'from': data.from, 'to': data.to},
+        'type': 'range'
+      }));
+	}
 
     vm.search();
   }  
@@ -256,8 +254,6 @@ var Query = function (vm, query) {
         return false;
       }
     });
-
-    vm.search();
   } 
 
   self.getFacetFilter = function (widget_id) {
@@ -394,20 +390,25 @@ var Collection = function (vm, collection) {
   
   self.toggleFacet = function (facet_field, event) {
 	vm.query.removeFilter(ko.mapping.fromJS({'id': facet_field.id})); // Reset filter query
+	var hasChanged = false;
 	
     if (facet_field.properties.canRange()) {
-	   if (facet_field.type() == 'field' && facet_field.properties.sort() == 'desc') {
+	   if (facet_field.type() == 'field' && facet_field.properties.sort() == 'asc') {
 		 facet_field.type('range');
+		 hasChanged = true;
 	   } else if (facet_field.type() == 'range' && facet_field.properties.sort() == 'desc') {
-	     facet_field.type('field'); 
+	    facet_field.type('field')
+	     hasChanged = true;
        }
     }
-   
-    if (facet_field.properties.sort() == 'desc') {
-      facet_field.properties.sort('asc');
-    } else {
-      facet_field.properties.sort('desc');
-    }   
+
+    if (! hasChanged) {
+      if (facet_field.properties.sort() == 'desc') {
+        facet_field.properties.sort('asc');
+      } else {
+        facet_field.properties.sort('desc');
+      }   
+    }
    
     $(event.target).button('loading');
     vm.search();