Quellcode durchsuchen

HUE-8178 [dashboard] Fix time stays static.

jdesjean vor 7 Jahren
Ursprung
Commit
40a97f3378

+ 15 - 50
desktop/core/src/desktop/static/desktop/js/ko.charts.js

@@ -707,22 +707,10 @@
     var previous = new Date(9999,11,31);
     var minDiff = 5.1;
     return d3v3.time.format.utc.multi([
-      ["%S %Y-%m-%dT%H:%M", function(d) {
+      ["%H:%M:%S %Y-%m-%d", function(d) {
         var domain = xAxis.domain();
-        var chart = _chart;
-        var domainDiff = Math.abs(domain[domain.length - 1] - domain[0]);
-        var isFirst = previous > d || d == domain[0];
-        var result = isFirst && domainDiff < MINUTE_MS * minDiff;
-        if (result) {
-          previous = d;
-        }
-        return result;
-      }],
-      ["%H:%M %Y-%m-%d", function(d) {
-        var domain = xAxis.domain();
-        var domainDiff = Math.abs(domain[domain.length - 1] - domain[0]);
-        var isFirst = previous > d || d == domain[0];
-        var result = isFirst && domainDiff < HOUR_MS * minDiff;
+        var isFirst = (previous > d || d == domain[0]) && moment(d).utc().seconds();
+        var result = isFirst;
         if (result) {
           previous = d;
         }
@@ -730,38 +718,7 @@
       }],
       ["%H:%M %Y-%m-%d", function(d) {
         var domain = xAxis.domain();
-        var domainDiff = Math.abs(domain[domain.length - 1] - domain[0]);
-        var isFirst = previous > d || d == domain[0];
-        var result = isFirst && domainDiff < DAY_MS * minDiff;
-        if (result) {
-          previous = d;
-        }
-        return result;
-      }],
-      ["%d %Y-%m", function(d) {
-        var domain = xAxis.domain();
-        var domainDiff = Math.abs(domain[domain.length - 1] - domain[0]);
-        var isFirst = previous > d || d == domain[0];
-        var result = isFirst && domainDiff < MONTH_MS * minDiff;
-        if (result) {
-          previous = d;
-        }
-        return result;
-      }],
-      ["%m %Y", function(d) {
-        var domain = xAxis.domain();
-        var domainDiff = Math.abs(domain[domain.length - 1] - domain[0]);
-        var isFirst = previous > d || d == domain[0];
-        var result = isFirst && domainDiff < YEAR_MS * minDiff;
-        if (result) {
-          previous = d;
-        }
-        return result;
-      }],
-      ["%Y", function(d) {
-        var test = xAxis;
-        var domain = xAxis.domain();
-        var isFirst = previous > d || d == domain[0];
+        var isFirst = (previous > d || d == domain[0]);
         var result = isFirst;
         if (result) {
           previous = d;
@@ -784,6 +741,14 @@
         }
         return result;
       }],
+      ["%H:%M:%S %Y-%m-%d", function(d) {
+        var previousDiff = Math.abs(d - previous);
+        var result = moment(previous).utc().date() !== moment(d).utc().date() && previousDiff < WEEK_MS && moment(d).utc().seconds();
+        if (result) {
+          previous = d;
+        }
+        return result;
+      }],
       ["%H:%M %Y-%m-%d", function(d) {
         var previousDiff = Math.abs(d - previous);
         var result = moment(previous).utc().date() !== moment(d).utc().date() && previousDiff < WEEK_MS;
@@ -792,9 +757,9 @@
         }
         return result;
       }],
-      ["%H:%M", function(d) {
+      ["%H:%M:%S", function(d) {
         var previousDiff = Math.abs(d - previous);
-        var result = moment(previous).utc().minutes() !== moment(d).utc().minutes() && previousDiff < WEEK_MS;
+        var result = (moment(previous).utc().hours() !== moment(d).utc().hours() || moment(previous).utc().minutes() !== moment(d).utc().minutes()) && previousDiff < WEEK_MS && moment(d).utc().seconds();
         if (result) {
           previous = d;
         }
@@ -802,7 +767,7 @@
       }],
       ["%H:%M", function(d) {
         var previousDiff = Math.abs(d - previous);
-        var result = moment(previous).utc().hours() !== moment(d).utc().hours() && previousDiff < WEEK_MS;
+        var result = (moment(previous).utc().hours() !== moment(d).utc().hours() || moment(previous).utc().minutes() !== moment(d).utc().minutes()) && previousDiff < WEEK_MS;
         if (result) {
           previous = d;
         }

+ 82 - 41
desktop/libs/dashboard/src/dashboard/facet_builder.py

@@ -37,36 +37,42 @@ MINUTE_MS = SECOND_MS * 60
 HOUR_MS = MINUTE_MS * 60
 DAY_MS = HOUR_MS * 24
 WEEK_MS = DAY_MS * 7
-MONTH_MS = DAY_MS * 30.5
+MONTH_MS = DAY_MS * 30
 YEAR_MS = DAY_MS * 365
 TIME_INTERVALS = [
-  {'ms': SECOND_MS * 1, 'coeff': '1', 'unit': 'SECONDS'},
-  {'ms': SECOND_MS * 2, 'coeff': '2', 'unit': 'SECONDS'},
-  {'ms': SECOND_MS * 5, 'coeff': '5', 'unit': 'SECONDS'},
-  {'ms': SECOND_MS * 10, 'coeff': '10', 'unit': 'SECONDS'},
-  {'ms': SECOND_MS * 15, 'coeff': '15', 'unit': 'SECONDS'},
-  {'ms': SECOND_MS * 30, 'coeff': '30', 'unit': 'SECONDS'},
-  {'ms': MINUTE_MS * 1, 'coeff': '1', 'unit': 'MINUTES'},
-  {'ms': MINUTE_MS * 2, 'coeff': '2', 'unit': 'MINUTES'},
-  {'ms': MINUTE_MS * 5, 'coeff': '5', 'unit': 'MINUTES'},
-  {'ms': MINUTE_MS * 10, 'coeff': '10', 'unit': 'MINUTES'},
-  {'ms': MINUTE_MS * 15, 'coeff': '15', 'unit': 'MINUTES'},
-  {'ms': MINUTE_MS * 30, 'coeff': '30', 'unit': 'MINUTES'},
-  {'ms': HOUR_MS * 1, 'coeff': '1', 'unit': 'HOURS'},
-  {'ms': HOUR_MS * 2, 'coeff': '2', 'unit': 'HOURS'},
-  {'ms': HOUR_MS * 4, 'coeff': '4', 'unit': 'HOURS'},
-  {'ms': HOUR_MS * 6, 'coeff': '6', 'unit': 'HOURS'},
-  {'ms': HOUR_MS * 8, 'coeff': '8', 'unit': 'HOURS'},
-  {'ms': HOUR_MS * 12, 'coeff': '12', 'unit': 'HOURS'},
-  {'ms': DAY_MS * 1, 'coeff': '1', 'unit': 'DAYS'},
-  {'ms': DAY_MS * 2, 'coeff': '2', 'unit': 'MONTHS'},
-  {'ms': WEEK_MS * 1, 'coeff': '7', 'unit': 'DAYS'},
-  {'ms': WEEK_MS * 2, 'coeff': '14', 'unit': 'DAYS'},
-  {'ms': MONTH_MS * 1, 'coeff': '1', 'unit': 'MONTHS'},
-  {'ms': MONTH_MS * 2, 'coeff': '2', 'unit': 'MONTHS'},
-  {'ms': MONTH_MS * 3, 'coeff': '3', 'unit': 'MONTHS'},
-  {'ms': MONTH_MS * 6, 'coeff': '6', 'unit': 'MONTHS'},
-  {'ms': YEAR_MS * 1, 'coeff': '1', 'unit': 'YEARS'}];
+  {'ms': SECOND_MS * 1, 'base_ms': SECOND_MS, 'coeff': '1', 'unit': 'SECONDS'},
+  {'ms': SECOND_MS * 2, 'base_ms': SECOND_MS, 'coeff': '2', 'unit': 'SECONDS'},
+  {'ms': SECOND_MS * 5, 'base_ms': SECOND_MS, 'coeff': '5', 'unit': 'SECONDS'},
+  {'ms': SECOND_MS * 10, 'base_ms': SECOND_MS, 'coeff': '10', 'unit': 'SECONDS'},
+  {'ms': SECOND_MS * 15, 'base_ms': SECOND_MS, 'coeff': '15', 'unit': 'SECONDS'},
+  {'ms': SECOND_MS * 30, 'base_ms': SECOND_MS, 'coeff': '30', 'unit': 'SECONDS'},
+  {'ms': MINUTE_MS * 1, 'base_ms': MINUTE_MS, 'coeff': '1', 'unit': 'MINUTES'},
+  {'ms': MINUTE_MS * 2, 'base_ms': MINUTE_MS, 'coeff': '2', 'unit': 'MINUTES'},
+  {'ms': MINUTE_MS * 5, 'base_ms': MINUTE_MS, 'coeff': '5', 'unit': 'MINUTES'},
+  {'ms': MINUTE_MS * 10, 'base_ms': MINUTE_MS, 'coeff': '10', 'unit': 'MINUTES'},
+  {'ms': MINUTE_MS * 15, 'base_ms': MINUTE_MS, 'coeff': '15', 'unit': 'MINUTES'},
+  {'ms': MINUTE_MS * 30, 'base_ms': MINUTE_MS, 'coeff': '30', 'unit': 'MINUTES'},
+  {'ms': HOUR_MS * 1, 'base_ms': HOUR_MS, 'coeff': '1', 'unit': 'HOURS'},
+  {'ms': HOUR_MS * 2, 'base_ms': HOUR_MS, 'coeff': '2', 'unit': 'HOURS'},
+  {'ms': HOUR_MS * 4, 'base_ms': HOUR_MS, 'coeff': '4', 'unit': 'HOURS'},
+  {'ms': HOUR_MS * 6, 'base_ms': HOUR_MS, 'coeff': '6', 'unit': 'HOURS'},
+  {'ms': HOUR_MS * 8, 'base_ms': HOUR_MS, 'coeff': '8', 'unit': 'HOURS'},
+  {'ms': HOUR_MS * 12, 'base_ms': HOUR_MS, 'coeff': '12', 'unit': 'HOURS'},
+  {'ms': DAY_MS * 1, 'base_ms': DAY_MS, 'coeff': '1', 'unit': 'DAYS'},
+  {'ms': DAY_MS * 2, 'base_ms': DAY_MS, 'coeff': '2', 'unit': 'DAYS'},
+  {'ms': WEEK_MS * 1, 'base_ms': DAY_MS, 'coeff': '7', 'unit': 'DAYS'},
+  {'ms': WEEK_MS * 2, 'base_ms': DAY_MS, 'coeff': '14', 'unit': 'DAYS'},
+  {'ms': MONTH_MS * 1, 'base_ms': MONTH_MS, 'coeff': '1', 'unit': 'MONTHS'},
+  {'ms': MONTH_MS * 2, 'base_ms': MONTH_MS, 'coeff': '2', 'unit': 'MONTHS'},
+  {'ms': MONTH_MS * 3, 'base_ms': MONTH_MS, 'coeff': '3', 'unit': 'MONTHS'},
+  {'ms': MONTH_MS * 6, 'base_ms': MONTH_MS, 'coeff': '6', 'unit': 'MONTHS'},
+  {'ms': YEAR_MS * 1, 'base_ms': YEAR_MS, 'coeff': '1', 'unit': 'YEARS'}];
+TIME_INTERVAL_SORTED = [{'key': 'microsecond', 'value': 0},
+                        {'key': 'second', 'value': 0},
+                        {'key': 'minute', 'value': 0},
+                        {'key': 'hour', 'value': 0},
+                        {'key': 'day', 'value': 1},
+                        {'key': 'month', 'value': 1}]
 TIME_INTERVALS_MS = {
   'SECONDS': SECOND_MS,
   'MINUTES': MINUTE_MS,
@@ -97,14 +103,17 @@ def _get_interval(domain_ms, SLOTS):
   biggest_interval_is_too_small = domain_ms / biggest_interval['ms'] > SLOTS
   if biggest_interval_is_too_small:
     coeff = min(ceil(domain_ms / SLOTS), 100) # If we go over 100 years, something has gone wrong.
-    return '+' + str(coeff) + 'YEARS'
+    return {'ms': YEAR_MS * coeff, 'base_ms': YEAR_MS, 'coeff': coeff, 'unit': 'YEARS'}
 
   for i in range(len(TIME_INTERVALS) - 2, 0, -1):
     slots = domain_ms / TIME_INTERVALS[i]['ms']
     if slots > SLOTS:
-      return '+' + TIME_INTERVALS[i + 1]['coeff'] + TIME_INTERVALS[i + 1]['unit']
+      return TIME_INTERVALS[i + 1]
 
-  return '+' + TIME_INTERVALS[0]['coeff'] + TIME_INTERVALS[0]['unit'];
+  return TIME_INTERVALS[0]
+
+def _format_interval(interval):
+  return '+' + interval['coeff'] + interval['unit']
 
 def _get_interval_duration(text):
   regex = re.search('.*-(\d*)(.*)', text)
@@ -115,6 +124,27 @@ def _get_interval_duration(text):
       return TIME_INTERVALS_MS[groups[1]] * int(groups[0])
   return 0
 
+def _clamp_date(interval, time):
+  gap_duration_lower = interval['unit'].lower()
+  gap_duration_lowers = gap_duration_lower[:-1]  # Removes 's'
+  for time_interval in TIME_INTERVAL_SORTED:
+    if time_interval['key'] != gap_duration_lowers:
+      kwargs = {time_interval['key']: time_interval['value']}
+      time = time.replace(**kwargs)
+    else:
+      break
+  return time
+
+def _get_next_interval(interval, start_time, do_at_least_once):
+  gap_duration_lower = interval['unit'].lower()
+  gap_duration_lowers = gap_duration_lower[:-1]  # Removes 's'
+  gap_duration = int(interval['coeff'])
+  time = start_time
+  while getattr(time, gap_duration_lowers) % gap_duration or (do_at_least_once and time == start_time): # Do while
+    kwargs = {gap_duration_lower: 1}
+    time = time + timedelta(milliseconds=interval['base_ms'])
+  return time
+
 def _compute_range_facet(widget_type, stat_facet, properties, start=None, end=None, gap=None, SLOTS=0, window_size=None):
     if SLOTS == 0:
       if widget_type == 'pie-widget' or widget_type == 'pie2-widget':
@@ -157,7 +187,9 @@ def _compute_range_facet(widget_type, stat_facet, properties, start=None, end=No
       is_date = True
       stats_min = stat_facet['min']
       stats_max = stat_facet['max']
+      start_was_none = False
       if start is None:
+        start_was_none = True
         start = stats_min
       start = re.sub('\.\d\d?\d?Z$', 'Z', start)
       try:
@@ -166,8 +198,7 @@ def _compute_range_facet(widget_type, stat_facet, properties, start=None, end=No
       except Exception, e:
         LOG.error('Bad date: %s' % e)
         start_ts = datetime.strptime('1970-01-01T00:00:00Z', '%Y-%m-%dT%H:%M:%SZ')
-      start = start_ts.strftime('%Y-%m-%dT%H:%M:%SZ')
-      stats_min = min(stats_min, start)
+
       if end is None:
         end = stats_max
       end = re.sub('\.\d\d?\d?Z$', 'Z', end)
@@ -178,18 +209,28 @@ def _compute_range_facet(widget_type, stat_facet, properties, start=None, end=No
         LOG.error('Bad date: %s' % e)
         end_ts = datetime.strptime('2050-01-01T00:00:00Z', '%Y-%m-%dT%H:%M:%SZ')
       end = end_ts.strftime('%Y-%m-%dT%H:%M:%SZ')
-      stats_max = max(stats_max, end)
       domain_ms = (mktime(end_ts.timetuple()) - mktime(start_ts.timetuple())) * 1000
-
-      gap = _get_interval(domain_ms, SLOTS)
+      interval = _get_interval(domain_ms, SLOTS)
+      start = start_ts.strftime('%Y-%m-%dT%H:%M:%SZ')
+      gap = _format_interval(interval)
+      if start_was_none:
+        start_ts = _clamp_date(interval, start_ts)
+        start = start_ts.strftime('%Y-%m-%dT%H:%M:%SZ')
+        stats_max = end
+        stats_min = start
+      else:
+        start = start_ts.strftime('%Y-%m-%dT%H:%M:%SZ')
     elif stat_facet['max'] == 'NOW':
       is_date = True
       domain_ms = _get_interval_duration(stat_facet['min'])
-      start = stat_facet['min']
-      end = stat_facet['max']
-      stats_min = start
-      stats_max = end
-      gap = _get_interval(domain_ms, SLOTS)
+      interval = _get_interval(domain_ms, SLOTS)
+      gap = _format_interval(interval)
+      end_ts = datetime.utcnow()
+      end_ts_clamped = _clamp_date(interval, end_ts)
+      end_ts = _get_next_interval(interval, end_ts_clamped, end_ts_clamped != end_ts)
+      start_ts = end_ts - timedelta(milliseconds=domain_ms)
+      stats_max = end = end_ts.strftime('%Y-%m-%dT%H:%M:%SZ')
+      stats_min = start = start_ts.strftime('%Y-%m-%dT%H:%M:%SZ')
 
     properties.update({
       'min': stats_min,

+ 4 - 9
desktop/libs/dashboard/src/dashboard/static/dashboard/js/search.ko.js

@@ -1737,17 +1737,12 @@ var Collection = function (vm, collection) {
 
     vm.query.removeFilter(ko.mapping.fromJS({'id': facet_id}));
     if (facet.properties.canRange()) {
-      facet.properties.gap(facet.properties.initial_gap());
-      facet.properties.start(facet.properties.initial_start());
-      facet.properties.end(facet.properties.initial_end());
-      facet.properties.min(facet.properties.initial_start());
-      facet.properties.max(facet.properties.initial_end());
+      facet.properties.start(facet.properties.min());
+      facet.properties.end(facet.properties.max());
       if (facet.properties.facets) {
         var nestedFacet = facet.properties.facets()[0];
-        nestedFacet.start(facet.properties.initial_start());
-        nestedFacet.end(facet.properties.initial_end());
-        nestedFacet.min(facet.properties.initial_start());
-        nestedFacet.max(facet.properties.initial_end());
+        nestedFacet.start(facet.properties.min());
+        nestedFacet.end(facet.properties.max());
       }
     }
     vm.search();

+ 38 - 19
desktop/libs/libsolr/src/libsolr/api.py

@@ -125,7 +125,7 @@ class SolrApi(object):
           }
 
           if facet['properties']['canRange'] or timeFilter and timeFilter['time_field'] == facet['field'] and (facet['id'] not in timeFilter['time_filter_overrides'] or facet['widgetType'] != 'histogram-widget'):
-            keys.update(self._get_time_filter_query(timeFilter, facet))
+            keys.update(self._get_time_filter_query(timeFilter, facet, collection))
 
           params += (
              ('facet.range', '{!key=%(key)s ex=%(id)s f.%(field)s.facet.range.start=%(start)s f.%(field)s.facet.range.end=%(end)s f.%(field)s.facet.range.gap=%(gap)s f.%(field)s.facet.mincount=%(mincount)s}%(field)s' % keys),
@@ -145,7 +145,7 @@ class SolrApi(object):
         elif facet['type'] == 'nested':
           _f = {}
           if facet['properties']['facets']:
-            self._n_facet_dimension(facet, _f, facet['properties']['facets'], 1, timeFilter, can_range = facet['properties']['canRange'])
+            self._n_facet_dimension(facet, _f, facet['properties']['facets'], 1, timeFilter, collection, can_range = facet['properties']['canRange'])
 
           if facet['properties'].get('domain'):
             if facet['properties']['domain'].get('blockParent') or facet['properties']['domain'].get('blockChildren'):
@@ -273,7 +273,7 @@ class SolrApi(object):
     return self._get_json(response)
 
 
-  def _n_facet_dimension(self, widget, _f, facets, dim, timeFilter, can_range=None):
+  def _n_facet_dimension(self, widget, _f, facets, dim, timeFilter, collection, can_range=None):
     facet = facets[0]
     f_name = 'dim_%02d:%s' % (dim, facet['field'])
 
@@ -316,16 +316,16 @@ class SolrApi(object):
         # Only on dim 1 currently
         if can_range or (timeFilter and timeFilter['time_field'] == facet['field'] and (widget['id'] not in timeFilter['time_filter_overrides'])): # or facet['widgetType'] != 'bucket-widget'):
           facet['widgetType'] = widget['widgetType']
-          _f[f_name].update(self._get_time_filter_query(timeFilter, facet))
+          _f[f_name].update(self._get_time_filter_query(timeFilter, facet, collection))
 
       if widget['widgetType'] == 'tree2-widget' and facets[-1]['aggregate']['function'] != 'count':
         _f['subcount'] = self._get_aggregate_function(facets[-1])
 
       if len(facets) > 1: # Get n+1 dimension
         if facets[1]['aggregate']['function'] == 'count':
-          self._n_facet_dimension(widget, _f[f_name], facets[1:], dim + 1, timeFilter)
+          self._n_facet_dimension(widget, _f[f_name], facets[1:], dim + 1, timeFilter, collection)
         else:
-          self._n_facet_dimension(widget, _f[f_name], facets[1:], dim, timeFilter)
+          self._n_facet_dimension(widget, _f[f_name], facets[1:], dim, timeFilter, collection)
     else:
       agg_function = self._get_aggregate_function(facet)
       _f['facet'] = {
@@ -336,7 +336,7 @@ class SolrApi(object):
           agg_function = self._get_aggregate_function(_f_agg)
           _f['facet']['agg_%02d_%02d:%s' % (dim, i, agg_function)] = agg_function
         else:
-          self._n_facet_dimension(widget, _f, facets[i:], dim + 1, timeFilter) # Get n+1 dimension
+          self._n_facet_dimension(widget, _f, facets[i:], dim + 1, timeFilter, collection) # Get n+1 dimension
           break
 
 
@@ -937,26 +937,45 @@ class SolrApi(object):
 
     return props
 
-  def _get_time_filter_query(self, timeFilter, facet):
+  def _get_time_filter_query(self, timeFilter, facet, collection):
     properties = facet.get('properties', facet)
-    if not timeFilter:
+    if not timeFilter and properties['slot'] != 0:
       props = {}
-      stat_facet = {'min': properties['start'], 'max': properties['end']}
-      _compute_range_facet(facet['widgetType'], stat_facet, props, stat_facet['min'], stat_facet['max'],
+      # If the start & end are equal to min/max, then we want to show the whole domain. Since min/max can change, we fetch latest values and update start/end
+      if properties['start'] == properties['min'] and properties['end'] == properties['max']:
+        stats_json = self.stats(collection['name'], [facet['field']])
+        stat_facet = stats_json['stats']['stats_fields'][facet['field']]
+        properties['start'] = None
+        properties['end'] = None
+      else: # The user has zoomed in. Only show that section.
+        stat_facet = {'min': properties['min'], 'max': properties['max']}
+      _compute_range_facet(facet['widgetType'], stat_facet, props, properties['start'], properties['end'],
                            SLOTS=properties['slot'])
       return {
+        'min': '%(min)s' % props,
+        'max': '%(max)s' % props,
+        'start': '%(start)s' % props,
+        'end': '%(end)s' % props,
         'gap': '%(gap)s' % props,  # add a 'auto'
       }
-    elif 'fixed' in timeFilter or properties['slot'] != 0:
+    elif timeFilter:
       props = {}
-      stat_facet = {'min': timeFilter['from'], 'max': timeFilter['to']}
-      _compute_range_facet(facet['widgetType'], stat_facet, props, stat_facet['min'], stat_facet['max'], SLOTS = properties['slot'])
-      gap = props['gap']
-      unit = re.split('\d+', gap)[1]
+
+      # If the start & end are equal to min/max, then we want to show the whole domain (either interval now-x or static)
+      # In that case use timeFilter values
+      if properties['start'] == properties['min'] and properties['end'] == properties['max']:
+        stat_facet = {'min': timeFilter['from'], 'max': timeFilter['to']}
+        properties['start'] = None
+        properties['end'] = None
+      else: # the user has zoomed in. Only show that section.
+        stat_facet = {'min': properties['min'], 'max': properties['max']}
+      _compute_range_facet(facet['widgetType'], stat_facet, props, properties['start'], properties['end'], SLOTS = properties['slot'])
       return {
-        'start': '%(from)s/%(unit)s' % {'from': timeFilter['from'], 'unit': unit},
-        'end': '%(to)s/%(unit)s' % {'to': timeFilter['to'], 'unit': unit},
-        'gap': '%(gap)s' % props, # add a 'auto'
+        'start': '%(start)s' % props,
+        'end': '%(end)s' % props,
+        'gap': '%(gap)s' % props,  # add a 'auto'
+        'min': '%(min)s' % props,
+        'max': '%(max)s' % props,
       }
     else:
       gap = timeFilter['gap'][facet['widgetType']]