You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ma...@apache.org on 2018/04/06 23:19:19 UTC

[incubator-superset] branch master updated: Pass granularity from backend to frontend as ISO duration (#4755)

This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 426c34e  Pass granularity from backend to frontend as ISO duration (#4755)
426c34e is described below

commit 426c34ee8643929cdda5a462c6d04289e73cd68b
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Fri Apr 6 16:19:17 2018 -0700

    Pass granularity from backend to frontend as ISO duration (#4755)
    
    * Add ISO duration to time grains
    
    * Use ISO duration
    
    * Remove debugging code
    
    * Add module to yarn.lock
    
    * Remove autolint
    
    * Druid granularity as ISO
    
    * Remove dangling comma
---
 .../assets/javascripts/explore/stores/controls.jsx |  30 +--
 superset/assets/package.json                       |   1 +
 .../visualizations/deckgl/layers/scatter.jsx       |  26 +--
 superset/assets/yarn.lock                          |   4 +
 superset/connectors/sqla/models.py                 |   2 +-
 superset/db_engine_specs.py                        | 257 +++++++++++++--------
 superset/models/core.py                            |   2 +-
 7 files changed, 183 insertions(+), 139 deletions(-)

diff --git a/superset/assets/javascripts/explore/stores/controls.jsx b/superset/assets/javascripts/explore/stores/controls.jsx
index 65dd2f6..b6d784e 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -699,21 +699,21 @@ export const controls = {
     freeForm: true,
     label: t('Time Granularity'),
     default: 'one day',
-    choices: formatSelectOptions([
-      'all',
-      '5 seconds',
-      '30 seconds',
-      '1 minute',
-      '5 minutes',
-      '1 hour',
-      '6 hour',
-      '1 day',
-      '7 days',
-      'week',
-      'week_starting_sunday',
-      'week_ending_saturday',
-      'month',
-    ]),
+    choices: [
+      [null, 'all'],
+      ['PT5S', '5 seconds'],
+      ['PT30S', '30 seconds'],
+      ['PT1M', '1 minute'],
+      ['PT5M', '5 minutes'],
+      ['PT1H', '1 hour'],
+      ['PT6H', '6 hour'],
+      ['P1D', '1 day'],
+      ['P7D', '7 days'],
+      ['P1W', 'week'],
+      ['P1W', 'week_starting_sunday'],
+      ['P1W', 'week_ending_saturday'],
+      ['P1M', 'month'],
+    ],
     description: t('The time granularity for the visualization. Note that you ' +
     'can type and use simple natural language as in `10 seconds`, ' +
     '`1 day` or `56 weeks`'),
diff --git a/superset/assets/package.json b/superset/assets/package.json
index 6d39a6b..69e36ee 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -79,6 +79,7 @@
     "object.entries": "^1.0.4",
     "object.keys": "^0.1.0",
     "object.values": "^1.0.4",
+    "parse-iso-duration": "^1.0.0",
     "po2json": "^0.4.5",
     "prop-types": "^15.6.0",
     "react": "^15.6.2",
diff --git a/superset/assets/visualizations/deckgl/layers/scatter.jsx b/superset/assets/visualizations/deckgl/layers/scatter.jsx
index 3d8e99c..052a7ab 100644
--- a/superset/assets/visualizations/deckgl/layers/scatter.jsx
+++ b/superset/assets/visualizations/deckgl/layers/scatter.jsx
@@ -4,6 +4,7 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 import PropTypes from 'prop-types';
 
+import parseIsoDuration from 'parse-iso-duration';
 import { ScatterplotLayer } from 'deck.gl';
 
 import AnimatableDeckGLContainer from '../AnimatableDeckGLContainer';
@@ -14,27 +15,6 @@ import { getColorFromScheme, hexToRGB } from '../../../javascripts/modules/color
 import { unitToRadius } from '../../../javascripts/modules/geo';
 import sandboxedEval from '../../../javascripts/modules/sandbox';
 
-function getStep(timeGrain) {
-  // grain in milliseconds
-  const MINUTE = 60 * 1000;
-  const HOUR = 60 * MINUTE;
-  const DAY = 24 * HOUR;
-  const WEEK = 7 * DAY;
-  const MONTH = 30 * DAY;
-  const YEAR = 365 * DAY;
-
-  const milliseconds = {
-    'Time Column': MINUTE,
-    min: MINUTE,
-    hour: HOUR,
-    day: DAY,
-    week: WEEK,
-    month: MONTH,
-    year: YEAR,
-  };
-
-  return milliseconds[timeGrain];
-}
 
 function getPoints(data) {
   return data.map(d => d.position);
@@ -117,7 +97,7 @@ class DeckGLScatter extends React.PureComponent {
   /* eslint-disable no-unused-vars */
   static getDerivedStateFromProps(nextProps, prevState) {
     const fd = nextProps.slice.formData;
-    const timeGrain = fd.time_grain_sqla || fd.granularity || 'min';
+    const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';
 
     // find start and end based on the data
     const timestamps = nextProps.payload.data.features.map(f => f.__timestamp);
@@ -125,7 +105,7 @@ class DeckGLScatter extends React.PureComponent {
     let end = Math.max(...timestamps);
 
     // lock start and end to the closest steps
-    const step = getStep(timeGrain);
+    const step = parseIsoDuration(timeGrain);
     start -= start % step;
     end += step - end % step;
 
diff --git a/superset/assets/yarn.lock b/superset/assets/yarn.lock
index 6ca7fe1..f77c1c6 100644
--- a/superset/assets/yarn.lock
+++ b/superset/assets/yarn.lock
@@ -6528,6 +6528,10 @@ parse-glob@^3.0.4:
     is-extglob "^1.0.0"
     is-glob "^2.0.0"
 
+parse-iso-duration@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/parse-iso-duration/-/parse-iso-duration-1.0.0.tgz#b923ab898a8ff8f42bdc9ee5db6e22808c48a864"
+
 parse-json@^2.1.0, parse-json@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py
index 0f1640d..7033048 100644
--- a/superset/connectors/sqla/models.py
+++ b/superset/connectors/sqla/models.py
@@ -376,7 +376,7 @@ class SqlaTable(Model, BaseDatasource):
         if self.type == 'table':
             grains = self.database.grains() or []
             if grains:
-                grains = [(g.name, g.name) for g in grains]
+                grains = [(g.duration, g.name) for g in grains]
             d['granularity_sqla'] = utils.choicify(self.dttm_cols)
             d['time_grain_sqla'] = grains
         return d
diff --git a/superset/db_engine_specs.py b/superset/db_engine_specs.py
index c9006bc..85ff9db 100644
--- a/superset/db_engine_specs.py
+++ b/superset/db_engine_specs.py
@@ -47,7 +47,7 @@ config = app.config
 tracking_url_trans = conf.get('TRACKING_URL_TRANSFORMER')
 hive_poll_interval = conf.get('HIVE_POLL_INTERVAL')
 
-Grain = namedtuple('Grain', 'name label function')
+Grain = namedtuple('Grain', 'name label function duration')
 
 
 class LimitMethod(object):
@@ -287,23 +287,23 @@ class PostgresBaseEngineSpec(BaseEngineSpec):
     engine = ''
 
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
         Grain('second', _('second'),
-              "DATE_TRUNC('second', {col}) AT TIME ZONE 'UTC'"),
+              "DATE_TRUNC('second', {col}) AT TIME ZONE 'UTC'", 'PT1S'),
         Grain('minute', _('minute'),
-              "DATE_TRUNC('minute', {col}) AT TIME ZONE 'UTC'"),
+              "DATE_TRUNC('minute', {col}) AT TIME ZONE 'UTC'", 'PT1M'),
         Grain('hour', _('hour'),
-              "DATE_TRUNC('hour', {col}) AT TIME ZONE 'UTC'"),
+              "DATE_TRUNC('hour', {col}) AT TIME ZONE 'UTC'", 'PT1H'),
         Grain('day', _('day'),
-              "DATE_TRUNC('day', {col}) AT TIME ZONE 'UTC'"),
+              "DATE_TRUNC('day', {col}) AT TIME ZONE 'UTC'", 'P1D'),
         Grain('week', _('week'),
-              "DATE_TRUNC('week', {col}) AT TIME ZONE 'UTC'"),
+              "DATE_TRUNC('week', {col}) AT TIME ZONE 'UTC'", 'P1W'),
         Grain('month', _('month'),
-              "DATE_TRUNC('month', {col}) AT TIME ZONE 'UTC'"),
+              "DATE_TRUNC('month', {col}) AT TIME ZONE 'UTC'", 'P1M'),
         Grain('quarter', _('quarter'),
-              "DATE_TRUNC('quarter', {col}) AT TIME ZONE 'UTC'"),
+              "DATE_TRUNC('quarter', {col}) AT TIME ZONE 'UTC'", 'P0.25Y'),
         Grain('year', _('year'),
-              "DATE_TRUNC('year', {col}) AT TIME ZONE 'UTC'"),
+              "DATE_TRUNC('year', {col}) AT TIME ZONE 'UTC'", 'P1Y'),
     )
 
     @classmethod
@@ -346,14 +346,14 @@ class OracleEngineSpec(PostgresBaseEngineSpec):
     engine = 'oracle'
 
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
-        Grain('minute', _('minute'), "TRUNC(TO_DATE({col}), 'MI')"),
-        Grain('hour', _('hour'), "TRUNC(TO_DATE({col}), 'HH')"),
-        Grain('day', _('day'), "TRUNC(TO_DATE({col}), 'DDD')"),
-        Grain('week', _('week'), "TRUNC(TO_DATE({col}), 'WW')"),
-        Grain('month', _('month'), "TRUNC(TO_DATE({col}), 'MONTH')"),
-        Grain('quarter', _('quarter'), "TRUNC(TO_DATE({col}), 'Q')"),
-        Grain('year', _('year'), "TRUNC(TO_DATE({col}), 'YEAR')"),
+        Grain('Time Column', _('Time Column'), '{col}', None),
+        Grain('minute', _('minute'), "TRUNC(TO_DATE({col}), 'MI')", 'PT1M'),
+        Grain('hour', _('hour'), "TRUNC(TO_DATE({col}), 'HH')", 'PT1H'),
+        Grain('day', _('day'), "TRUNC(TO_DATE({col}), 'DDD')", 'P1D'),
+        Grain('week', _('week'), "TRUNC(TO_DATE({col}), 'WW')", 'P1W'),
+        Grain('month', _('month'), "TRUNC(TO_DATE({col}), 'MONTH')", 'P1M'),
+        Grain('quarter', _('quarter'), "TRUNC(TO_DATE({col}), 'Q')", 'P0.25Y'),
+        Grain('year', _('year'), "TRUNC(TO_DATE({col}), 'YEAR')", 'P1Y'),
     )
 
     @classmethod
@@ -366,36 +366,44 @@ class OracleEngineSpec(PostgresBaseEngineSpec):
 class Db2EngineSpec(BaseEngineSpec):
     engine = 'ibm_db_sa'
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
         Grain('second', _('second'),
               'CAST({col} as TIMESTAMP)'
-              ' - MICROSECOND({col}) MICROSECONDS'),
+              ' - MICROSECOND({col}) MICROSECONDS',
+              'PT1S'),
         Grain('minute', _('minute'),
               'CAST({col} as TIMESTAMP)'
               ' - SECOND({col}) SECONDS'
-              ' - MICROSECOND({col}) MICROSECONDS'),
+              ' - MICROSECOND({col}) MICROSECONDS',
+              'PT1M'),
         Grain('hour', _('hour'),
               'CAST({col} as TIMESTAMP)'
               ' - MINUTE({col}) MINUTES'
               ' - SECOND({col}) SECONDS'
-              ' - MICROSECOND({col}) MICROSECONDS '),
+              ' - MICROSECOND({col}) MICROSECONDS ',
+              'PT1H'),
         Grain('day', _('day'),
               'CAST({col} as TIMESTAMP)'
               ' - HOUR({col}) HOURS'
               ' - MINUTE({col}) MINUTES'
               ' - SECOND({col}) SECONDS'
-              ' - MICROSECOND({col}) MICROSECONDS '),
+              ' - MICROSECOND({col}) MICROSECONDS ',
+              'P1D'),
         Grain('week', _('week'),
-              '{col} - (DAYOFWEEK({col})) DAYS'),
+              '{col} - (DAYOFWEEK({col})) DAYS',
+              'P1W'),
         Grain('month', _('month'),
-              '{col} - (DAY({col})-1) DAYS'),
+              '{col} - (DAY({col})-1) DAYS',
+              'P1M'),
         Grain('quarter', _('quarter'),
               '{col} - (DAY({col})-1) DAYS'
               ' - (MONTH({col})-1) MONTHS'
-              ' + ((QUARTER({col})-1) * 3) MONTHS'),
+              ' + ((QUARTER({col})-1) * 3) MONTHS',
+              'P0.25Y'),
         Grain('year', _('year'),
               '{col} - (DAY({col})-1) DAYS'
-              ' - (MONTH({col})-1) MONTHS'),
+              ' - (MONTH({col})-1) MONTHS',
+              'P1Y'),
     )
 
     @classmethod
@@ -410,14 +418,17 @@ class Db2EngineSpec(BaseEngineSpec):
 class SqliteEngineSpec(BaseEngineSpec):
     engine = 'sqlite'
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
         Grain('hour', _('hour'),
-              "DATETIME(STRFTIME('%Y-%m-%dT%H:00:00', {col}))"),
-        Grain('day', _('day'), 'DATE({col})'),
+              "DATETIME(STRFTIME('%Y-%m-%dT%H:00:00', {col}))",
+              'PT1H'),
+        Grain('day', _('day'), 'DATE({col})', 'P1D'),
         Grain('week', _('week'),
-              "DATE({col}, -strftime('%w', {col}) || ' days')"),
+              "DATE({col}, -strftime('%w', {col}) || ' days')",
+              'P1W'),
         Grain('month', _('month'),
-              "DATE({col}, -strftime('%d', {col}) || ' days', '+1 day')"),
+              "DATE({col}, -strftime('%d', {col}) || ' days', '+1 day')",
+              'P1M'),
     )
 
     @classmethod
@@ -460,26 +471,34 @@ class MySQLEngineSpec(BaseEngineSpec):
     engine = 'mysql'
     cursor_execute_kwargs = {'args': {}}
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
         Grain('second', _('second'), 'DATE_ADD(DATE({col}), '
               'INTERVAL (HOUR({col})*60*60 + MINUTE({col})*60'
-              ' + SECOND({col})) SECOND)'),
+              ' + SECOND({col})) SECOND)',
+              'PT1S'),
         Grain('minute', _('minute'), 'DATE_ADD(DATE({col}), '
-              'INTERVAL (HOUR({col})*60 + MINUTE({col})) MINUTE)'),
+              'INTERVAL (HOUR({col})*60 + MINUTE({col})) MINUTE)',
+              'PT1M'),
         Grain('hour', _('hour'), 'DATE_ADD(DATE({col}), '
-              'INTERVAL HOUR({col}) HOUR)'),
-        Grain('day', _('day'), 'DATE({col})'),
+              'INTERVAL HOUR({col}) HOUR)',
+              'PT1H'),
+        Grain('day', _('day'), 'DATE({col})', 'P1D'),
         Grain('week', _('week'), 'DATE(DATE_SUB({col}, '
-              'INTERVAL DAYOFWEEK({col}) - 1 DAY))'),
+              'INTERVAL DAYOFWEEK({col}) - 1 DAY))',
+              'P1W'),
         Grain('month', _('month'), 'DATE(DATE_SUB({col}, '
-              'INTERVAL DAYOFMONTH({col}) - 1 DAY))'),
+              'INTERVAL DAYOFMONTH({col}) - 1 DAY))',
+              'P1M'),
         Grain('quarter', _('quarter'), 'MAKEDATE(YEAR({col}), 1) '
-              '+ INTERVAL QUARTER({col}) QUARTER - INTERVAL 1 QUARTER'),
+              '+ INTERVAL QUARTER({col}) QUARTER - INTERVAL 1 QUARTER',
+              'P0.25Y'),
         Grain('year', _('year'), 'DATE(DATE_SUB({col}, '
-              'INTERVAL DAYOFYEAR({col}) - 1 DAY))'),
+              'INTERVAL DAYOFYEAR({col}) - 1 DAY))',
+              'P1Y'),
         Grain('week_start_monday', _('week_start_monday'),
               'DATE(DATE_SUB({col}, '
-              'INTERVAL DAYOFWEEK(DATE_SUB({col}, INTERVAL 1 DAY)) - 1 DAY))'),
+              'INTERVAL DAYOFWEEK(DATE_SUB({col}, INTERVAL 1 DAY)) - 1 DAY))',
+              'P1W'),
     )
 
     @classmethod
@@ -516,27 +535,39 @@ class PrestoEngineSpec(BaseEngineSpec):
     cursor_execute_kwargs = {'parameters': None}
 
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
         Grain('second', _('second'),
-              "date_trunc('second', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('second', CAST({col} AS TIMESTAMP))",
+              'PT1S'),
         Grain('minute', _('minute'),
-              "date_trunc('minute', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('minute', CAST({col} AS TIMESTAMP))",
+              'PT1M'),
         Grain('hour', _('hour'),
-              "date_trunc('hour', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('hour', CAST({col} AS TIMESTAMP))",
+              'PT1H'),
         Grain('day', _('day'),
-              "date_trunc('day', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('day', CAST({col} AS TIMESTAMP))",
+              'P1D'),
         Grain('week', _('week'),
-              "date_trunc('week', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('week', CAST({col} AS TIMESTAMP))",
+              'P1W'),
         Grain('month', _('month'),
-              "date_trunc('month', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('month', CAST({col} AS TIMESTAMP))",
+              'P1M'),
         Grain('quarter', _('quarter'),
-              "date_trunc('quarter', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('quarter', CAST({col} AS TIMESTAMP))",
+              'P0.25Y'),
         Grain('week_ending_saturday', _('week_ending_saturday'),
               "date_add('day', 5, date_trunc('week', date_add('day', 1, "
-              'CAST({col} AS TIMESTAMP))))'),
+              'CAST({col} AS TIMESTAMP))))',
+              'P1W'),
         Grain('week_start_sunday', _('week_start_sunday'),
               "date_add('day', -1, date_trunc('week', "
-              "date_add('day', 1, CAST({col} AS TIMESTAMP))))"),
+              "date_add('day', 1, CAST({col} AS TIMESTAMP))))",
+              'P1W'),
+        Grain('year', _('year'),
+              "date_trunc('year', CAST({col} AS TIMESTAMP))",
+              'P1Y'),
     )
 
     @classmethod
@@ -1054,27 +1085,37 @@ class MssqlEngineSpec(BaseEngineSpec):
     epoch_to_dttm = "dateadd(S, {col}, '1970-01-01')"
 
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
         Grain('second', _('second'), 'DATEADD(second, '
-              "DATEDIFF(second, '2000-01-01', {col}), '2000-01-01')"),
+              "DATEDIFF(second, '2000-01-01', {col}), '2000-01-01')",
+              'PT1S'),
         Grain('minute', _('minute'), 'DATEADD(minute, '
-              'DATEDIFF(minute, 0, {col}), 0)'),
+              'DATEDIFF(minute, 0, {col}), 0)',
+              'PT1M'),
         Grain('5 minute', _('5 minute'), 'DATEADD(minute, '
-              'DATEDIFF(minute, 0, {col}) / 5 * 5, 0)'),
+              'DATEDIFF(minute, 0, {col}) / 5 * 5, 0)',
+              'PT5M'),
         Grain('half hour', _('half hour'), 'DATEADD(minute, '
-              'DATEDIFF(minute, 0, {col}) / 30 * 30, 0)'),
+              'DATEDIFF(minute, 0, {col}) / 30 * 30, 0)',
+              'PT0.5H'),
         Grain('hour', _('hour'), 'DATEADD(hour, '
-              'DATEDIFF(hour, 0, {col}), 0)'),
+              'DATEDIFF(hour, 0, {col}), 0)',
+              'PT1H'),
         Grain('day', _('day'), 'DATEADD(day, '
-              'DATEDIFF(day, 0, {col}), 0)'),
+              'DATEDIFF(day, 0, {col}), 0)',
+              'P1D'),
         Grain('week', _('week'), 'DATEADD(week, '
-              'DATEDIFF(week, 0, {col}), 0)'),
+              'DATEDIFF(week, 0, {col}), 0)',
+              'P1W'),
         Grain('month', _('month'), 'DATEADD(month, '
-              'DATEDIFF(month, 0, {col}), 0)'),
+              'DATEDIFF(month, 0, {col}), 0)',
+              'P1M'),
         Grain('quarter', _('quarter'), 'DATEADD(quarter, '
-              'DATEDIFF(quarter, 0, {col}), 0)'),
+              'DATEDIFF(quarter, 0, {col}), 0)',
+              'P0.25Y'),
         Grain('year', _('year'), 'DATEADD(year, '
-              'DATEDIFF(year, 0, {col}), 0)'),
+              'DATEDIFF(year, 0, {col}), 0)',
+              'P1Y'),
     )
 
     @classmethod
@@ -1086,27 +1127,36 @@ class AthenaEngineSpec(BaseEngineSpec):
     engine = 'awsathena'
 
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
         Grain('second', _('second'),
-              "date_trunc('second', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('second', CAST({col} AS TIMESTAMP))",
+              'PT1S'),
         Grain('minute', _('minute'),
-              "date_trunc('minute', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('minute', CAST({col} AS TIMESTAMP))",
+              'PT1M'),
         Grain('hour', _('hour'),
-              "date_trunc('hour', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('hour', CAST({col} AS TIMESTAMP))",
+              'PT1H'),
         Grain('day', _('day'),
-              "date_trunc('day', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('day', CAST({col} AS TIMESTAMP))",
+              'P1D'),
         Grain('week', _('week'),
-              "date_trunc('week', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('week', CAST({col} AS TIMESTAMP))",
+              'P1W'),
         Grain('month', _('month'),
-              "date_trunc('month', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('month', CAST({col} AS TIMESTAMP))",
+              'P1M'),
         Grain('quarter', _('quarter'),
-              "date_trunc('quarter', CAST({col} AS TIMESTAMP))"),
+              "date_trunc('quarter', CAST({col} AS TIMESTAMP))",
+              'P0.25Y'),
         Grain('week_ending_saturday', _('week_ending_saturday'),
               "date_add('day', 5, date_trunc('week', date_add('day', 1, "
-              'CAST({col} AS TIMESTAMP))))'),
+              'CAST({col} AS TIMESTAMP))))',
+              'P1W'),
         Grain('week_start_sunday', _('week_start_sunday'),
               "date_add('day', -1, date_trunc('week', "
-              "date_add('day', 1, CAST({col} AS TIMESTAMP))))"),
+              "date_add('day', 1, CAST({col} AS TIMESTAMP))))",
+              'P1W'),
     )
 
     @classmethod
@@ -1132,23 +1182,31 @@ class ClickHouseEngineSpec(BaseEngineSpec):
     time_secondary_columns = True
     time_groupby_inline = True
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
         Grain('minute', _('minute'),
-              'toStartOfMinute(toDateTime({col}))'),
+              'toStartOfMinute(toDateTime({col}))',
+              'PT1M'),
         Grain('5 minute', _('5 minute'),
-              'toDateTime(intDiv(toUInt32(toDateTime({col})), 300)*300)'),
+              'toDateTime(intDiv(toUInt32(toDateTime({col})), 300)*300)',
+              'PT5M'),
         Grain('10 minute', _('10 minute'),
-              'toDateTime(intDiv(toUInt32(toDateTime({col})), 600)*600)'),
+              'toDateTime(intDiv(toUInt32(toDateTime({col})), 600)*600)',
+              'PT10M'),
         Grain('hour', _('hour'),
-              'toStartOfHour(toDateTime({col}))'),
+              'toStartOfHour(toDateTime({col}))',
+              'PT1H'),
         Grain('day', _('day'),
-              'toStartOfDay(toDateTime({col}))'),
+              'toStartOfDay(toDateTime({col}))',
+              'P1D'),
         Grain('month', _('month'),
-              'toStartOfMonth(toDateTime({col}))'),
+              'toStartOfMonth(toDateTime({col}))',
+              'P1M'),
         Grain('quarter', _('quarter'),
-              'toStartOfQuarter(toDateTime({col}))'),
+              'toStartOfQuarter(toDateTime({col}))',
+              'P0.25Y'),
         Grain('year', _('year'),
-              'toStartOfYear(toDateTime({col}))'),
+              'toStartOfYear(toDateTime({col}))',
+              'P1Y'),
     )
 
     @classmethod
@@ -1169,15 +1227,16 @@ class BQEngineSpec(BaseEngineSpec):
     engine = 'bigquery'
 
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
-        Grain('second', _('second'), 'TIMESTAMP_TRUNC({col}, SECOND)'),
-        Grain('minute', _('minute'), 'TIMESTAMP_TRUNC({col}, MINUTE)'),
-        Grain('hour', _('hour'), 'TIMESTAMP_TRUNC({col}, HOUR)'),
-        Grain('day', _('day'), 'TIMESTAMP_TRUNC({col}, DAY)'),
-        Grain('week', _('week'), 'TIMESTAMP_TRUNC({col}, WEEK)'),
-        Grain('month', _('month'), 'TIMESTAMP_TRUNC({col}, MONTH)'),
-        Grain('quarter', _('quarter'), 'TIMESTAMP_TRUNC({col}, QUARTER)'),
-        Grain('year', _('year'), 'TIMESTAMP_TRUNC({col}, YEAR)'),
+        Grain('Time Column', _('Time Column'), '{col}', None),
+        Grain('second', _('second'), 'TIMESTAMP_TRUNC({col}, SECOND)', 'PT1S'),
+        Grain('minute', _('minute'), 'TIMESTAMP_TRUNC({col}, MINUTE)', 'PT1M'),
+        Grain('hour', _('hour'), 'TIMESTAMP_TRUNC({col}, HOUR)', 'PT1H'),
+        Grain('day', _('day'), 'TIMESTAMP_TRUNC({col}, DAY)', 'P1D'),
+        Grain('week', _('week'), 'TIMESTAMP_TRUNC({col}, WEEK)', 'P1W'),
+        Grain('month', _('month'), 'TIMESTAMP_TRUNC({col}, MONTH)', 'P1M'),
+        Grain('quarter', _('quarter'),
+              'TIMESTAMP_TRUNC({col}, QUARTER)', 'P0.25Y'),
+        Grain('year', _('year'), 'TIMESTAMP_TRUNC({col}, YEAR)', 'P1Y'),
     )
 
     @classmethod
@@ -1201,14 +1260,14 @@ class ImpalaEngineSpec(BaseEngineSpec):
     engine = 'impala'
 
     time_grains = (
-        Grain('Time Column', _('Time Column'), '{col}'),
-        Grain('minute', _('minute'), "TRUNC({col}, 'MI')"),
-        Grain('hour', _('hour'), "TRUNC({col}, 'HH')"),
-        Grain('day', _('day'), "TRUNC({col}, 'DD')"),
-        Grain('week', _('week'), "TRUNC({col}, 'WW')"),
-        Grain('month', _('month'), "TRUNC({col}, 'MONTH')"),
-        Grain('quarter', _('quarter'), "TRUNC({col}, 'Q')"),
-        Grain('year', _('year'), "TRUNC({col}, 'YYYY')"),
+        Grain('Time Column', _('Time Column'), '{col}', None),
+        Grain('minute', _('minute'), "TRUNC({col}, 'MI')", 'PT1M'),
+        Grain('hour', _('hour'), "TRUNC({col}, 'HH')", 'PT1H'),
+        Grain('day', _('day'), "TRUNC({col}, 'DD')", 'P1D'),
+        Grain('week', _('week'), "TRUNC({col}, 'WW')", 'P1W'),
+        Grain('month', _('month'), "TRUNC({col}, 'MONTH')", 'P1M'),
+        Grain('quarter', _('quarter'), "TRUNC({col}, 'Q')", 'P0.25Y'),
+        Grain('year', _('year'), "TRUNC({col}, 'YYYY')", 'P1Y'),
     )
 
     @classmethod
diff --git a/superset/models/core.py b/superset/models/core.py
index 6eef48c..5ff737e 100644
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -786,7 +786,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
         return self.db_engine_spec.time_grains
 
     def grains_dict(self):
-        return {grain.name: grain for grain in self.grains()}
+        return {grain.duration: grain for grain in self.grains()}
 
     def get_extra(self):
         extra = {}

-- 
To stop receiving notification emails like this one, please contact
maximebeauchemin@apache.org.