superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From christ...@apache.org
Subject [incubator-superset] branch lyft-release-sp8 updated: Merge master into lyft-release-sp8 (#7484)
Date Fri, 10 May 2019 19:57:21 GMT
This is an automated email from the ASF dual-hosted git repository.

christine pushed a commit to branch lyft-release-sp8
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/lyft-release-sp8 by this push:
     new a1c0051  Merge master into lyft-release-sp8 (#7484)
a1c0051 is described below

commit a1c00517321b4759223e70959324950709e41bfc
Author: Dave Smith <dave.a.smith@gmail.com>
AuthorDate: Fri May 10 12:57:08 2019 -0700

    Merge master into lyft-release-sp8 (#7484)
    
    * filter out all nan series (#7313)
    
    * improve not rich tooltip (#7345)
    
    * Create issue_label_bot.yaml (#7341)
    
    * fix: do not save colors without a color scheme (#7347)
    
    * [wtforms] Strip leading/trailing whitespace (#7084)
    
    * [schema] Updating the datasources schema (#5451)
    
    * limit tables/views returned if schema is not provided (#7358)
    
    * limit tables/views returned if schema is not provided
    
    * fix typo
    
    * improve code performance
    
    * handle the case when table name or view name does not present a schema
    
    * Add type anno (#7342)
    
    * Updated local dev instructions to include missing step
    
    * First pass at type annotations
    
    * [schema] Updating the base column schema (#5452)
    
    * Update 937d04c16b64_update_datasources.py (#7361)
    
    * Feature flag for client cache (#7348)
    
    * Feature flag for client cache
    
    * Fix integration test
    
    * Revert "Fix integration test"
    
    This reverts commit 58434ab98a015d6e96db4a97f26255aa282d989d.
    
    * Feature flag for client cache
    
    * Fix integration tests
    
    * Add feature flag to config.py
    
    * Add another feature check
    
    * Fix more integration tests
    
    * Fix raw HTML in SliceAdder (#7338)
    
    * remove backendSync.json (#7331)
    
    * [bubbles] issue when using duplicated metrics (#7087)
    
    * SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04 (#7359)
    
    * SUPERSET-8: Update text in docs copyright footer (#7360)
    
    * SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04
    
    * SUPERSET-8: Extra text in docs copyright footer
    
    * [schema] Adding commits and removing unnecessary foreign-key definitions (#7371)
    
    *  Store last selected dashboard in sessionStorage (#7181)
    
    * Store last selected dashboard in sessionStorage
    
    * Fix tests
    
    * [schema] Updating the base metric schema (#5453)
    
    * Fix NoneType bug & fill the test recipients with original recipients if empty (#7365)
    
    * Added living goods as among the users of Superset (#7407)
    
    * Added living goods as among the users of Superset
    
    Living Goods is a non profit organisation with operation in africa and the middle east. We work in community health use data heavily on day to day. Superset is our platform of choice for dashboards.
    
    * Update README.md
    
    * [dashboard] allow user re-order top-level tabs (#7390)
    
    * [SQL Lab] Increase timeout threshold for offline check (#7411)
    
    * Bump FAB to 2.0.0 (#7323)
    
    * Bump FAB to 2.0.0
    
    * [tests] whitelist SecurityApi login and refresh endpoints
    
    * [style] Fix, C812 missing trailing commas
    
    * [security] Remove SUPERSET_UPDATE_PERMS flag
    
    Registering sources needs to be performed after the views are
    initialized on UPDATE_PERMS=False configuration
    
    * [docs] New, FAB_UPDATE_PERMS and flask fab cli
    
    * [docs] Fix, db upgrade needs to come first, create-admin needs a db
    
    * [cli] New, superset init bootstraps all permissions for FAB and Superset
    
    * [style] Fix, flakes
    
    * [annotations] Improves UX on annotation validation, start_dttm, end_dttm (#7326)
    
    * Setting renderTrigger on label_colors (#7410)
    
    * Refactor out controlUtils.js module + unit tests (#7350)
    
    * [WiP]refactor out a controlUtils.js file
    
    * unit tests
    
    * add missing license
    
    * Addressing comments
    
    * feature: see Presto row and array data types (#7413)
    
    * Merge lastest from master into lyft-release-sp8 (#7405)
    
    * filter out all nan series (#7313)
    
    * improve not rich tooltip (#7345)
    
    * Create issue_label_bot.yaml (#7341)
    
    * fix: do not save colors without a color scheme (#7347)
    
    * [wtforms] Strip leading/trailing whitespace (#7084)
    
    * [schema] Updating the datasources schema (#5451)
    
    * limit tables/views returned if schema is not provided (#7358)
    
    * limit tables/views returned if schema is not provided
    
    * fix typo
    
    * improve code performance
    
    * handle the case when table name or view name does not present a schema
    
    * Add type anno (#7342)
    
    * Updated local dev instructions to include missing step
    
    * First pass at type annotations
    
    * [schema] Updating the base column schema (#5452)
    
    * Update 937d04c16b64_update_datasources.py (#7361)
    
    * Feature flag for client cache (#7348)
    
    * Feature flag for client cache
    
    * Fix integration test
    
    * Revert "Fix integration test"
    
    This reverts commit 58434ab98a015d6e96db4a97f26255aa282d989d.
    
    * Feature flag for client cache
    
    * Fix integration tests
    
    * Add feature flag to config.py
    
    * Add another feature check
    
    * Fix more integration tests
    
    * Fix raw HTML in SliceAdder (#7338)
    
    * remove backendSync.json (#7331)
    
    * [bubbles] issue when using duplicated metrics (#7087)
    
    * SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04 (#7359)
    
    * SUPERSET-8: Update text in docs copyright footer (#7360)
    
    * SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04
    
    * SUPERSET-8: Extra text in docs copyright footer
    
    * [schema] Adding commits and removing unnecessary foreign-key definitions (#7371)
    
    *  Store last selected dashboard in sessionStorage (#7181)
    
    * Store last selected dashboard in sessionStorage
    
    * Fix tests
    
    * [schema] Updating the base metric schema (#5453)
    
    * Fix NoneType bug & fill the test recipients with original recipients if empty (#7365)
    
    * feat: see Presto row and array data types (#7391)
    
    * feat: see Presto row and array data types
    
    * fix: address PR comments
    
    * fix: lint and build issues
    
    * fix: add types
    
    * add stronger type hints where possible
    
    * fix: lint issues and add select_star func in Hive
    
    * add missing pkg init
    
    * fix: build issues
    
    * fix: pylint issues
    
    * fix: use logging instead of print
    
    * Removed --console-log and superset runserver (#7421)
    
    * Fixes dashboard export button missing download and #7353 (#7427)
    
    * Added additional German translations to string file (#6604)
    
    * Added additional German translations to string file
    
    Updates to German translation files as per directions
    
    * Removed messages.json
    
    * [fix] Fixing SQL parsing issue (#7374)
    
    * add chinese translate (#7402)
    
    * Quick fix to address deadlock issue (#7434)
    
    * feat: view presto row objects in data grid (#7445)
    
    * Merge lastest from master into lyft-release-sp8 (#7405)
    
    * filter out all nan series (#7313)
    
    * improve not rich tooltip (#7345)
    
    * Create issue_label_bot.yaml (#7341)
    
    * fix: do not save colors without a color scheme (#7347)
    
    * [wtforms] Strip leading/trailing whitespace (#7084)
    
    * [schema] Updating the datasources schema (#5451)
    
    * limit tables/views returned if schema is not provided (#7358)
    
    * limit tables/views returned if schema is not provided
    
    * fix typo
    
    * improve code performance
    
    * handle the case when table name or view name does not present a schema
    
    * Add type anno (#7342)
    
    * Updated local dev instructions to include missing step
    
    * First pass at type annotations
    
    * [schema] Updating the base column schema (#5452)
    
    * Update 937d04c16b64_update_datasources.py (#7361)
    
    * Feature flag for client cache (#7348)
    
    * Feature flag for client cache
    
    * Fix integration test
    
    * Revert "Fix integration test"
    
    This reverts commit 58434ab98a015d6e96db4a97f26255aa282d989d.
    
    * Feature flag for client cache
    
    * Fix integration tests
    
    * Add feature flag to config.py
    
    * Add another feature check
    
    * Fix more integration tests
    
    * Fix raw HTML in SliceAdder (#7338)
    
    * remove backendSync.json (#7331)
    
    * [bubbles] issue when using duplicated metrics (#7087)
    
    * SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04 (#7359)
    
    * SUPERSET-8: Update text in docs copyright footer (#7360)
    
    * SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04
    
    * SUPERSET-8: Extra text in docs copyright footer
    
    * [schema] Adding commits and removing unnecessary foreign-key definitions (#7371)
    
    *  Store last selected dashboard in sessionStorage (#7181)
    
    * Store last selected dashboard in sessionStorage
    
    * Fix tests
    
    * [schema] Updating the base metric schema (#5453)
    
    * Fix NoneType bug & fill the test recipients with original recipients if empty (#7365)
    
    * feat: see Presto row and array data types (#7391)
    
    * feat: see Presto row and array data types
    
    * fix: address PR comments
    
    * fix: lint and build issues
    
    * fix: add types
    
    * Incorporate feedback from initial PR (prematurely merged to lyft-release-sp8) (#7415)
    
    * add stronger type hints where possible
    
    * fix: lint issues and add select_star func in Hive
    
    * add missing pkg init
    
    * fix: build issues
    
    * fix: pylint issues
    
    * fix: use logging instead of print
    
    * feat: view presto row objects in data grid
    
    * fix: address feedback
    
    * fix: spacing
    
    * feat: Scheduling queries from SQL Lab (#7416) (#7446)
    
    * Merge lastest from master into lyft-release-sp8 (#7405)
    
    * filter out all nan series (#7313)
    
    * improve not rich tooltip (#7345)
    
    * Create issue_label_bot.yaml (#7341)
    
    * fix: do not save colors without a color scheme (#7347)
    
    * [wtforms] Strip leading/trailing whitespace (#7084)
    
    * [schema] Updating the datasources schema (#5451)
    
    * limit tables/views returned if schema is not provided (#7358)
    
    * limit tables/views returned if schema is not provided
    
    * fix typo
    
    * improve code performance
    
    * handle the case when table name or view name does not present a schema
    
    * Add type anno (#7342)
    
    * Updated local dev instructions to include missing step
    
    * First pass at type annotations
    
    * [schema] Updating the base column schema (#5452)
    
    * Update 937d04c16b64_update_datasources.py (#7361)
    
    * Feature flag for client cache (#7348)
    
    * Feature flag for client cache
    
    * Fix integration test
    
    * Revert "Fix integration test"
    
    This reverts commit 58434ab98a015d6e96db4a97f26255aa282d989d.
    
    * Feature flag for client cache
    
    * Fix integration tests
    
    * Add feature flag to config.py
    
    * Add another feature check
    
    * Fix more integration tests
    
    * Fix raw HTML in SliceAdder (#7338)
    
    * remove backendSync.json (#7331)
    
    * [bubbles] issue when using duplicated metrics (#7087)
    
    * SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04 (#7359)
    
    * SUPERSET-8: Update text in docs copyright footer (#7360)
    
    * SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04
    
    * SUPERSET-8: Extra text in docs copyright footer
    
    * [schema] Adding commits and removing unnecessary foreign-key definitions (#7371)
    
    *  Store last selected dashboard in sessionStorage (#7181)
    
    * Store last selected dashboard in sessionStorage
    
    * Fix tests
    
    * [schema] Updating the base metric schema (#5453)
    
    * Fix NoneType bug & fill the test recipients with original recipients if empty (#7365)
    
    * feat: see Presto row and array data types (#7391)
    
    * feat: see Presto row and array data types
    
    * fix: address PR comments
    
    * fix: lint and build issues
    
    * fix: add types
    
    * Incorporate feedback from initial PR (prematurely merged to lyft-release-sp8) (#7415)
    
    * add stronger type hints where possible
    
    * fix: lint issues and add select_star func in Hive
    
    * add missing pkg init
    
    * fix: build issues
    
    * fix: pylint issues
    
    * fix: use logging instead of print
    
    * feat: view presto row objects in data grid
    
    * fix: address feedback
    
    * fix: spacing
    
    * Workaround for no results returned (#7442)
    
    * feat: view presto row objects in data grid (#7436)
    
    * feat: view presto row objects in data grid
    
    * fix: address feedback
    
    * fix: spacing
    
    * feat: Scheduling queries from SQL Lab (#7416)
    
    * Lightweight pipelines POC
    
    * Add docs
    
    * Minor fixes
    
    * Remove Lyft URL
    
    * Use enum
    
    * Minor fix
    
    * Fix unit tests
    
    * Mark props as required
    
    * feat: Add `validate_sql_json` endpoint for checking that a given sql query is valid for the chosen database (#7422) (#7462)
    
    merge from lyft-release-sp8 to master
    
    * Adds missing metric sum__SP_RUR_TOTL (#7452)
    
    * Late import for optional lib pyhive (#7471)
    
    * Late import for optional lib pyhive
    
    * fix
    
    * fix: calendar heatmap examples (#7375)
    
    Fixing a set of examples that trip on ValueError vs TypeError
    
    * bugfix: Improve support for special characters in schema and table names (#7297)
    
    * Bugfix to SQL Lab to support tables and schemas with characters that require quoting
    
    * Remove debugging prints
    
    * Add uri encoding to secondary tables call
    
    * Quote schema names for presto
    
    * Quote selected_schema on Snowflake, MySQL and Hive
    
    * Remove redundant parens
    
    * Add python unit tests
    
    * Add js unit test
    
    * Fix flake8 linting error
    
    * [dashboard] After update filter, trigger new queries when charts are visible (#7233)
    
    * trigger query when chart is visible
    
    * add integration test
    
    * fix: alter sql columns to long text #7463 (#7476)
    
    Merge lyft-release-sp8@7bfe7bc to master
    
    * Refactor ConsoleLog (#7428)
    
    * Revised Chinese translation (#7464)
    
    * add chinese translate
    
    * edit chinese translation
    
    * druid connector: avoid using 'dimensions' for scan queries (#7377)
    
    After the following PyDruid change (contained in version 0.5.2)
    the Superset Histogram charts rendered with Druid data are
    broken:
    
    druid-io/pydruid@0a59a70
    
    Bump the pydruid requirements accordingly in setup.py
    
    Issue: apache#7368
---
 CONTRIBUTING.md                                    |   13 +-
 README.md                                          |    1 +
 UPDATING.md                                        |    8 +-
 docs/faq.rst                                       |    4 +-
 docs/installation.rst                              |   17 +-
 requirements-dev.txt                               |    1 -
 requirements.txt                                   |   17 +-
 setup.py                                           |    5 +-
 superset/__init__.py                               |   29 +-
 .../integration/dashboard/dashboard.helper.js      |    3 +-
 .../cypress/integration/dashboard/index.test.js    |    2 +
 .../assets/cypress/integration/dashboard/tabs.js   |  157 ++
 .../javascripts/components/TableSelector_spec.jsx  |   20 +
 .../dashboard/actions/dashboardLayout_spec.js      |   67 +-
 .../dashboard/components/Dashboard_spec.jsx        |   10 +-
 .../components/ControlPanelsContainer_spec.jsx     |    3 +-
 .../components/ExploreViewContainer_spec.jsx       |   32 +-
 .../spec/javascripts/explore/controlUtils_spec.jsx |  164 ++
 .../assets/spec/javascripts/explore/store_spec.jsx |   66 +
 superset/assets/src/SqlLab/actions/sqlLab.js       |    6 +-
 .../src/SqlLab/components/QueryAutoRefresh.jsx     |    2 +-
 superset/assets/src/chart/Chart.jsx                |   48 +-
 superset/assets/src/components/TableSelector.jsx   |   10 +-
 .../src/dashboard/actions/dashboardLayout.js       |   29 +-
 .../assets/src/dashboard/components/Dashboard.jsx  |   19 +-
 .../src/dashboard/components/DashboardBuilder.jsx  |    8 +-
 .../src/dashboard/components/DashboardGrid.jsx     |    3 +
 .../dashboard/components/gridComponents/Chart.jsx  |   30 +-
 .../components/gridComponents/ChartHolder.jsx      |    2 +
 .../dashboard/components/gridComponents/Column.jsx |    2 +
 .../dashboard/components/gridComponents/Row.jsx    |    2 +
 .../dashboard/components/gridComponents/Tab.jsx    |    7 +-
 .../dashboard/components/gridComponents/Tabs.jsx   |    1 +
 .../assets/src/dashboard/containers/Dashboard.jsx  |    4 +-
 .../dashboard/containers/DashboardComponent.jsx    |    1 +
 .../explore/components/ExploreViewContainer.jsx    |    3 +-
 superset/assets/src/explore/controlUtils.js        |  124 +
 superset/assets/src/explore/controls.jsx           |    1 +
 .../assets/src/explore/reducers/exploreReducer.js  |   13 +-
 .../assets/src/explore/reducers/getInitialState.js |    3 +-
 superset/assets/src/explore/store.js               |  127 +-
 superset/assets/src/featureFlags.ts                |    1 -
 superset/assets/src/visualizations/deckgl/utils.js |    3 +
 superset/cli.py                                    |   98 +-
 superset/connectors/druid/models.py                |    3 +-
 superset/data/__init__.py                          |    1 +
 superset/data/tabbed_dashboard.py                  |  324 +++
 superset/data/world_bank.py                        |    2 +-
 superset/db_engine_specs.py                        |    7 +-
 superset/models/annotations.py                     |    2 +-
 superset/sql_parse.py                              |   39 +-
 superset/sql_validators/presto_db.py               |    2 +-
 superset/templates/superset/export_dashboards.html |   13 +-
 superset/translations/de/LC_MESSAGES/messages.json | 2419 +-------------------
 superset/translations/de/LC_MESSAGES/messages.mo   |  Bin 63955 -> 63813 bytes
 superset/translations/de/LC_MESSAGES/messages.po   |   14 +-
 superset/translations/zh/LC_MESSAGES/messages.mo   |  Bin 90011 -> 86446 bytes
 superset/translations/zh/LC_MESSAGES/messages.po   |  100 +-
 superset/utils/core.py                             |   20 +-
 superset/views/annotations.py                      |   32 +-
 superset/views/core.py                             |   23 +-
 superset/viz.py                                    |    2 +-
 tests/security_tests.py                            |    2 +
 tests/sql_parse_tests.py                           |    9 +
 tests/utils_tests.py                               |   16 +
 65 files changed, 1385 insertions(+), 2811 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cdca9cb..e620e6a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -328,10 +328,19 @@ FLASK_ENV=development flask run -p 8088 --with-threads --reload --debugger
 
 #### Logging to the browser console
 
-This feature is only available on Python 3. When debugging your application, you can have the server logs sent directly to the browser console:
+This feature is only available on Python 3. When debugging your application, you can have the server logs sent directly to the browser console using the [ConsoleLog](https://github.com/betodealmeida/consolelog) package. You need to mutate the app, by adding the following to your `config.py` or `superset_config.py`:
+
+```python
+from console_log import ConsoleLog
+
+def FLASK_APP_MUTATOR(app):
+    app.wsgi_app = ConsoleLog(app.wsgi_app, app.logger)
+```
+
+Then make sure you run your WSGI server using the right worker type:
 
 ```bash
-FLASK_ENV=development flask run -p 8088 --with-threads --reload --debugger --console-log
+FLASK_ENV=development gunicorn superset:app -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" -b 127.0.0.1:8088 --reload
 ```
 
 You can log anything to the browser console, including objects:
diff --git a/README.md b/README.md
index 68a7570..6b13d4e 100644
--- a/README.md
+++ b/README.md
@@ -195,6 +195,7 @@ the world know they are using Superset. Join our growing community!
  1. [Konfío](http://konfio.mx)
  1. [Kuaishou](https://www.kuaishou.com/)
  1. [Lime](https://www.limebike.com/)
+ 1. [Living Goods](https://www.livinggoods.org)
  1. [Lyft](https://www.lyft.com/)
  1. [Maieutical Labs](https://maieuticallabs.it)
  1. [Myra Labs](http://www.myralabs.com/)
diff --git a/UPDATING.md b/UPDATING.md
index f8d64ab..59b558b 100644
--- a/UPDATING.md
+++ b/UPDATING.md
@@ -21,7 +21,7 @@ under the License.
 This file documents any backwards-incompatible changes in Superset and
 assists people when migrating to a new version.
 
-## Superset 0.34.0
+## Next Version
 
 * [5451](https://github.com/apache/incubator-superset/pull/5451): a change
 which adds missing non-nullable fields to the `datasources` table. Depending on
@@ -31,7 +31,11 @@ the integrity of the data, manual intervention may be required.
 which adds missing non-nullable fields and uniqueness constraints to the
 `columns`and `table_columns` tables. Depending on the integrity of the data,
 manual intervention may be required.
-
+* `fabmanager` command line is deprecated since Flask-AppBuilder 2.0.0, use
+the new `flask fab <command>` integrated with *Flask cli*.
+* `SUPERSET_UPDATE_PERMS` environment variable was replaced by 
+`FAB_UPDATE_PERMS` config boolean key. To disable automatic
+creation of permissions set `FAB_UPDATE_PERMS = False` on config.
 * [5453](https://github.com/apache/incubator-superset/pull/5453): a change
 which adds missing non-nullable fields and uniqueness constraints to the metrics
 and sql_metrics tables. Depending on the integrity of the data, manual
diff --git a/docs/faq.rst b/docs/faq.rst
index d1f781f..426e0ab 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -186,8 +186,8 @@ by setting the ``stagger_refresh`` to ``false`` and modify the stagger period by
 Here, the entire dashboard will refresh at once if periodic refresh is on. The stagger time of
 2.5 seconds is ignored.
 
-Why does fabmanager or superset freezed/hung/not responding when started (my home directory is NFS mounted)?
-------------------------------------------------------------------------------------------------------------
+Why does 'flask fab' or superset freezed/hung/not responding when started (my home directory is NFS mounted)?
+-------------------------------------------------------------------------------------------------------------
 By default, superset creates and uses an sqlite database at ``~/.superset/superset.db``. Sqlite is known to `don't work well if used on NFS`__ due to broken file locking implementation on NFS.
 
 __ https://www.sqlite.org/lockingv3.html
diff --git a/docs/installation.rst b/docs/installation.rst
index 72fd5b0..c7c24fc 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -170,12 +170,13 @@ Follow these few simple steps to install Superset.::
     # Install superset
     pip install superset
 
-    # Create an admin user (you will be prompted to set a username, first and last name before setting a password)
-    fabmanager create-admin --app superset
-
     # Initialize the database
     superset db upgrade
 
+    # Create an admin user (you will be prompted to set a username, first and last name before setting a password)
+    $ export FLASK_APP=superset
+    flask fab create-admin
+
     # Load some data to play with
     superset load_examples
 
@@ -183,7 +184,7 @@ Follow these few simple steps to install Superset.::
     superset init
 
     # To start a development web server on port 8088, use -p to bind to another port
-    superset runserver -d
+    flask run -p 8080 --with-threads --reload --debugger
 
 
 After installation, you should be able to point your browser to the right
@@ -236,17 +237,11 @@ workers this creates a lot of contention and race conditions when defining
 permissions and views.
 
 To alleviate this issue, the automatic updating of permissions can be disabled
-by setting the environment variable
-`SUPERSET_UPDATE_PERMS` environment variable to `0`.
-The value `1` enables it, `0` disables it. Note if undefined the functionality
-is enabled to maintain backwards compatibility.
+by setting `FAB_UPDATE_PERMS = False` (defaults to True).
 
 In a production environment initialization could take on the following form:
 
-  export SUPERSET_UPDATE_PERMS=1
   superset init
-
-  export SUPERSET_UPDATE_PERMS=0
   gunicorn -w 10 ... superset:app
 
 Configuration behind a load balancer
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 1ef6617..857b9ad 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -14,7 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-console_log==0.2.10
 coverage==4.5.3
 flake8-commas==2.0.0
 flake8-import-order==0.18
diff --git a/requirements.txt b/requirements.txt
index bab2c19..1863a22 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,9 @@
 #
 alembic==1.0.0            # via flask-migrate
 amqp==2.3.2               # via kombu
+apispec[yaml]==1.2.0      # via flask-appbuilder
 asn1crypto==0.24.0        # via cryptography
+attrs==19.1.0             # via jsonschema
 babel==2.6.0              # via flask-babel
 billiard==3.5.0.4         # via celery
 bleach==3.0.2
@@ -21,10 +23,11 @@ croniter==0.3.29
 cryptography==2.4.2
 decorator==4.3.0          # via retry
 defusedxml==0.5.0         # via python3-openid
-flask-appbuilder==1.12.5
+flask-appbuilder==2.0.0
 flask-babel==0.11.1       # via flask-appbuilder
 flask-caching==1.4.0
 flask-compress==1.4.0
+flask-jwt-extended==3.18.1  # via flask-appbuilder
 flask-login==0.4.1        # via flask-appbuilder
 flask-migrate==2.1.1
 flask-openid==1.2.5       # via flask-appbuilder
@@ -38,19 +41,25 @@ idna==2.6
 isodate==0.6.0
 itsdangerous==0.24        # via flask
 jinja2==2.10              # via flask, flask-babel
+jsonschema==3.0.1         # via flask-appbuilder
 kombu==4.2.1              # via celery
 mako==1.0.7               # via alembic
 markdown==3.0
 markupsafe==1.0           # via jinja2, mako
+marshmallow-enum==1.4.1   # via flask-appbuilder
+marshmallow-sqlalchemy==0.16.2  # via flask-appbuilder
+marshmallow==2.19.2       # via flask-appbuilder, marshmallow-enum, marshmallow-sqlalchemy
 numpy==1.15.2             # via pandas
 pandas==0.23.4
 parsedatetime==2.0.0
 pathlib2==2.3.0
 polyline==1.3.2
+prison==0.1.0             # via flask-appbuilder
 py==1.7.0                 # via retry
 pycparser==2.19           # via cffi
 pydruid==0.5.2
-pyjwt==1.7.1              # via flask-appbuilder
+pyjwt==1.7.1              # via flask-appbuilder, flask-jwt-extended
+pyrsistent==0.14.11       # via jsonschema
 python-dateutil==2.6.1
 python-editor==1.0.3      # via alembic
 python-geohash==0.8.5
@@ -61,7 +70,7 @@ requests==2.20.0
 retry==0.9.2
 selenium==3.141.0
 simplejson==3.15.0
-six==1.11.0               # via bleach, cryptography, isodate, pathlib2, polyline, pydruid, python-dateutil, sqlalchemy-utils, wtforms-json
+six==1.11.0               # via bleach, cryptography, flask-jwt-extended, isodate, jsonschema, pathlib2, polyline, prison, pydruid, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json
 sqlalchemy-utils==0.32.21
 sqlalchemy==1.3.1
 sqlparse==0.2.4
@@ -69,6 +78,6 @@ unicodecsv==0.14.1
 urllib3==1.22             # via requests, selenium
 vine==1.1.4               # via amqp
 webencodings==0.5.1       # via bleach
-werkzeug==0.14.1          # via flask
+werkzeug==0.14.1          # via flask, flask-jwt-extended
 wtforms-json==0.3.3
 wtforms==2.2.1            # via flask-wtf, wtforms-json
diff --git a/setup.py b/setup.py
index 9c6278e..2cf3e39 100644
--- a/setup.py
+++ b/setup.py
@@ -77,7 +77,7 @@ setup(
         'croniter>=0.3.28',
         'cryptography>=2.4.2',
         'flask>=1.0.0, <2.0.0',
-        'flask-appbuilder>=1.12.5, <2.0.0',
+        'flask-appbuilder>=2.0.0, <2.3.0',
         'flask-caching',
         'flask-compress',
         'flask-migrate',
@@ -92,7 +92,7 @@ setup(
         'parsedatetime',
         'pathlib2',
         'polyline',
-        'pydruid>=0.4.3',
+        'pydruid>=0.5.2',
         'python-dateutil',
         'python-geohash',
         'pyyaml>=3.13',
@@ -108,7 +108,6 @@ setup(
     ],
     extras_require={
         'cors': ['flask-cors>=2.0.0'],
-        'console_log': ['console_log==0.2.10'],
         'hive': [
             'pyhive[hive]>=0.6.1',
             'tableschema',
diff --git a/superset/__init__.py b/superset/__init__.py
index 7d0df26..8f8936d 100644
--- a/superset/__init__.py
+++ b/superset/__init__.py
@@ -34,8 +34,7 @@ import wtforms_json
 from superset import config
 from superset.connectors.connector_registry import ConnectorRegistry
 from superset.security import SupersetSecurityManager
-from superset.utils.core import (
-    get_update_perms_flag, pessimistic_connection_handling, setup_cache)
+from superset.utils.core import pessimistic_connection_handling, setup_cache
 
 wtforms_json.init()
 
@@ -196,14 +195,14 @@ if not issubclass(custom_sm, SupersetSecurityManager):
          not FAB's security manager.
          See [4565] in UPDATING.md""")
 
-appbuilder = AppBuilder(
-    app,
-    db.session,
-    base_template='superset/base.html',
-    indexview=MyIndexView,
-    security_manager_class=custom_sm,
-    update_perms=get_update_perms_flag(),
-)
+with app.app_context():
+    appbuilder = AppBuilder(
+        app,
+        db.session,
+        base_template='superset/base.html',
+        indexview=MyIndexView,
+        security_manager_class=custom_sm,
+    )
 
 security_manager = appbuilder.sm
 
@@ -226,11 +225,6 @@ def is_feature_enabled(feature):
     return get_feature_flags().get(feature)
 
 
-# Registering sources
-module_datasource_map = app.config.get('DEFAULT_MODULE_DS_MAP')
-module_datasource_map.update(app.config.get('ADDITIONAL_MODULE_DS_MAP'))
-ConnectorRegistry.register_sources(module_datasource_map)
-
 # Flask-Compress
 if conf.get('ENABLE_FLASK_COMPRESS'):
     Compress(app)
@@ -242,3 +236,8 @@ if flask_app_mutator:
     flask_app_mutator(app)
 
 from superset import views  # noqa
+
+# Registering sources
+module_datasource_map = app.config.get('DEFAULT_MODULE_DS_MAP')
+module_datasource_map.update(app.config.get('ADDITIONAL_MODULE_DS_MAP'))
+ConnectorRegistry.register_sources(module_datasource_map)
diff --git a/superset/assets/cypress/integration/dashboard/dashboard.helper.js b/superset/assets/cypress/integration/dashboard/dashboard.helper.js
index 7e3788f..1c16a82 100644
--- a/superset/assets/cypress/integration/dashboard/dashboard.helper.js
+++ b/superset/assets/cypress/integration/dashboard/dashboard.helper.js
@@ -16,7 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-export const WORLD_HEALTH_DASHBOARD = '/superset/dashboard/world_health';
+export const WORLD_HEALTH_DASHBOARD = '/superset/dashboard/world_health/';
+export const TABBED_DASHBOARD = '/superset/dashboard/tabbed_dash/';
 
 export const CHECK_DASHBOARD_FAVORITE_ENDPOINT = '/superset/favstar/Dashboard/*/count';
 
diff --git a/superset/assets/cypress/integration/dashboard/index.test.js b/superset/assets/cypress/integration/dashboard/index.test.js
index 9763b91..cc608e7 100644
--- a/superset/assets/cypress/integration/dashboard/index.test.js
+++ b/superset/assets/cypress/integration/dashboard/index.test.js
@@ -22,6 +22,7 @@ import DashboardFavStarTest from './fav_star';
 import DashboardFilterTest from './filter';
 import DashboardLoadTest from './load';
 import DashboardSaveTest from './save';
+import DashboardTabsTest from './tabs';
 
 describe('Dashboard', () => {
   DashboardControlsTest();
@@ -30,4 +31,5 @@ describe('Dashboard', () => {
   DashboardFilterTest();
   DashboardLoadTest();
   DashboardSaveTest();
+  DashboardTabsTest();
 });
diff --git a/superset/assets/cypress/integration/dashboard/tabs.js b/superset/assets/cypress/integration/dashboard/tabs.js
new file mode 100644
index 0000000..5029a00
--- /dev/null
+++ b/superset/assets/cypress/integration/dashboard/tabs.js
@@ -0,0 +1,157 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { TABBED_DASHBOARD } from './dashboard.helper';
+
+export default () => describe('tabs', () => {
+  let filterId;
+  let treemapId;
+  let linechartId;
+  let boxplotId;
+
+  // cypress can not handle window.scrollTo
+  // https://github.com/cypress-io/cypress/issues/2761
+  // add this exception handler to pass test
+  const handleException = () => {
+    // return false to prevent the error from
+    // failing this test
+    cy.on('uncaught:exception', () => false);
+  };
+
+  beforeEach(() => {
+    cy.server();
+    cy.login();
+
+    cy.visit(TABBED_DASHBOARD);
+
+    cy.get('#app').then((data) => {
+      const bootstrapData = JSON.parse(data[0].dataset.bootstrap);
+      const dashboard = bootstrapData.dashboard_data;
+      filterId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'filter_box')).slice_id;
+      boxplotId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'box_plot')).slice_id;
+      treemapId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'treemap')).slice_id;
+      linechartId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'line')).slice_id;
+
+      const filterFormdata = {
+        slice_id: filterId,
+      };
+      const filterRequest = `/superset/explore_json/?form_data=${JSON.stringify(filterFormdata)}`;
+      cy.route('POST', filterRequest).as('filterRequest');
+
+      const treemapFormdata = {
+        slice_id: treemapId,
+      };
+      const treemapRequest = `/superset/explore_json/?form_data=${JSON.stringify(treemapFormdata)}`;
+      cy.route('POST', treemapRequest).as('treemapRequest');
+
+      const linechartFormdata = {
+        slice_id: linechartId,
+      };
+      const linechartRequest = `/superset/explore_json/?form_data=${JSON.stringify(linechartFormdata)}`;
+      cy.route('POST', linechartRequest).as('linechartRequest');
+
+      const boxplotFormdata = {
+        slice_id: boxplotId,
+      };
+      const boxplotRequest = `/superset/explore_json/?form_data=${JSON.stringify(boxplotFormdata)}`;
+      cy.route('POST', boxplotRequest).as('boxplotRequest');
+    });
+  });
+
+  it('should load charts when tab is visible', () => {
+    // landing in first tab, should see 2 charts
+    cy.wait('@filterRequest');
+    cy.get('.grid-container .filter_box').should('be.exist');
+    cy.wait('@treemapRequest');
+    cy.get('.grid-container .treemap').should('be.exist');
+    cy.get('.grid-container .box_plot').should('not.be.exist');
+    cy.get('.grid-container .line').should('not.be.exist');
+
+    // click row level tab, see 1 more chart
+    cy.get('.tab-content ul.nav.nav-tabs li')
+      .last()
+      .find('.editable-title input')
+      .click();
+    cy.wait('@linechartRequest');
+    cy.get('.grid-container .line').should('be.exist');
+
+    // click top level tab, see 1 more chart
+    handleException();
+    cy.get('.dashboard-component-tabs')
+      .first()
+      .find('ul.nav.nav-tabs li')
+      .last()
+      .find('.editable-title input')
+      .click();
+    cy.wait('@boxplotRequest');
+    cy.get('.grid-container .box_plot').should('be.exist');
+  });
+
+  it('should send new queries when tab becomes visible', () => {
+    // landing in first tab
+    cy.wait('@filterRequest');
+    cy.wait('@treemapRequest');
+
+    // creating route and stubbing filtered route
+    cy.route('POST', '/superset/explore_json/*').as('updatedChartRequest');
+
+    // apply filter
+    cy.get('.Select-control')
+      .first()
+      .find('input')
+      .first()
+      .type('South Asia{enter}', { force: true });
+
+    // send new query from same tab
+    cy.wait('@updatedChartRequest')
+      .then((xhr) => {
+        const requestFormData = xhr.request.body;
+        const requestParams = JSON.parse(requestFormData.get('form_data'));
+        expect(requestParams.extra_filters[0])
+          .deep.eq({ col: 'region', op: 'in', val: ['South Asia'] });
+      });
+
+    // click row level tab, send 1 more query
+    cy.get('.tab-content ul.nav.nav-tabs li')
+      .last()
+      .click();
+    cy.wait('@updatedChartRequest')
+      .then((xhr) => {
+        const requestFormData = xhr.request.body;
+        const requestParams = JSON.parse(requestFormData.get('form_data'));
+        expect(requestParams.extra_filters[0])
+          .deep.eq({ col: 'region', op: 'in', val: ['South Asia'] });
+      });
+
+    // click top level tab, send 1 more query
+    handleException();
+    cy.get('.dashboard-component-tabs')
+      .first()
+      .find('ul.nav.nav-tabs li')
+      .last()
+      .find('.editable-title input')
+      .click();
+    cy.wait('@updatedChartRequest')
+      .then((xhr) => {
+        const requestFormData = xhr.request.body;
+        const requestParams = JSON.parse(requestFormData.get('form_data'));
+        expect(requestParams.extra_filters[0])
+          .deep.eq({ col: 'region', op: 'in', val: ['South Asia'] });
+      });
+  });
+});
diff --git a/superset/assets/spec/javascripts/components/TableSelector_spec.jsx b/superset/assets/spec/javascripts/components/TableSelector_spec.jsx
index 46d5763..70e2cca 100644
--- a/superset/assets/spec/javascripts/components/TableSelector_spec.jsx
+++ b/superset/assets/spec/javascripts/components/TableSelector_spec.jsx
@@ -85,6 +85,7 @@ describe('TableSelector', () => {
         .getTableNamesBySubStr('')
         .then((data) => {
           expect(data).toEqual({ options: [] });
+          return Promise.resolve();
         }));
 
     it('should handle table name', () => {
@@ -104,6 +105,23 @@ describe('TableSelector', () => {
         .then((data) => {
           expect(fetchMock.calls(GET_TABLE_NAMES_GLOB)).toHaveLength(1);
           expect(data).toEqual(mockTableOptions);
+          return Promise.resolve();
+        });
+    });
+
+    it('should escape schema and table names', () => {
+      const GET_TABLE_GLOB = 'glob:*/superset/tables/1/*/*';
+      const mockTableOptions = { options: [table] };
+      wrapper.setProps({ schema: 'slashed/schema' });
+      fetchMock.get(GET_TABLE_GLOB, mockTableOptions, { overwriteRoutes: true });
+
+      return wrapper
+        .instance()
+        .getTableNamesBySubStr('slashed/table')
+        .then(() => {
+          expect(fetchMock.lastUrl(GET_TABLE_GLOB))
+            .toContain('/slashed%252Fschema/slashed%252Ftable');
+          return Promise.resolve();
         });
     });
   });
@@ -125,6 +143,7 @@ describe('TableSelector', () => {
         .fetchTables(true, 'birth_names')
         .then(() => {
           expect(wrapper.state().tableOptions).toHaveLength(3);
+          return Promise.resolve();
         });
     });
 
@@ -138,6 +157,7 @@ describe('TableSelector', () => {
           expect(wrapper.state().tableOptions).toEqual([]);
           expect(wrapper.state().tableOptions).toHaveLength(0);
           expect(mockedProps.handleError.callCount).toBe(1);
+          return Promise.resolve();
         });
     });
   });
diff --git a/superset/assets/spec/javascripts/dashboard/actions/dashboardLayout_spec.js b/superset/assets/spec/javascripts/dashboard/actions/dashboardLayout_spec.js
index d57207d..0dfca9c 100644
--- a/superset/assets/spec/javascripts/dashboard/actions/dashboardLayout_spec.js
+++ b/superset/assets/spec/javascripts/dashboard/actions/dashboardLayout_spec.js
@@ -39,7 +39,10 @@ import {
 } from '../../../../src/dashboard/actions/dashboardLayout';
 
 import { setUnsavedChanges } from '../../../../src/dashboard/actions/dashboardState';
-import { addInfoToast } from '../../../../src/messageToasts/actions';
+import {
+  addWarningToast,
+  ADD_TOAST,
+} from '../../../../src/messageToasts/actions';
 
 import {
   DASHBOARD_GRID_TYPE,
@@ -334,7 +337,9 @@ describe('dashboardLayout actions', () => {
 
       const thunk = handleComponentDrop(dropResult);
       thunk(dispatch, getState);
-      expect(dispatch.getCall(0).args[0].type).toEqual(addInfoToast('').type);
+      expect(dispatch.getCall(0).args[0].type).toEqual(
+        addWarningToast('').type,
+      );
 
       expect(dispatch.callCount).toBe(1);
     });
@@ -402,6 +407,64 @@ describe('dashboardLayout actions', () => {
 
       expect(dispatch.callCount).toBe(2);
     });
+
+    it('should dispatch a toast if drop top-level tab into nested tab', () => {
+      const { getState, dispatch } = setup({
+        dashboardLayout: {
+          present: {
+            [DASHBOARD_ROOT_ID]: {
+              children: ['TABS-ROOT_TABS'],
+              id: DASHBOARD_ROOT_ID,
+              type: 'ROOT',
+            },
+            'TABS-ROOT_TABS': {
+              children: ['TAB-iMppmTOQy', 'TAB-rt1y8cQ6K9', 'TAB-X_pnCIwPN'],
+              id: 'TABS-ROOT_TABS',
+              meta: {},
+              parents: ['ROOT_ID'],
+              type: TABS_TYPE,
+            },
+            'TABS-ROW_TABS': {
+              children: [
+                'TAB-dKIDBT03bQ',
+                'TAB-PtxY5bbTe',
+                'TAB-Wc2P-yGMz',
+                'TAB-U-xe_si7i',
+              ],
+              id: 'TABS-ROW_TABS',
+              meta: {},
+              parents: ['ROOT_ID', 'TABS-ROOT_TABS', 'TAB-X_pnCIwPN'],
+              type: TABS_TYPE,
+            },
+          },
+        },
+      });
+      const dropResult = {
+        source: {
+          id: 'TABS-ROOT_TABS',
+          index: 1,
+          type: TABS_TYPE,
+        },
+        destination: {
+          id: 'TABS-ROW_TABS',
+          index: 1,
+          type: TABS_TYPE,
+        },
+        dragging: {
+          id: 'TAB-rt1y8cQ6K9',
+          meta: { text: 'New Tab' },
+          type: 'TAB',
+        },
+      };
+
+      const thunk1 = handleComponentDrop(dropResult);
+      thunk1(dispatch, getState);
+
+      const thunk2 = dispatch.getCall(0).args[0];
+      thunk2(dispatch, getState);
+
+      expect(dispatch.getCall(1).args[0].type).toEqual(ADD_TOAST);
+    });
   });
 
   describe('undoLayoutAction', () => {
diff --git a/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx b/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx
index de637cd..05d3675 100644
--- a/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx
+++ b/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx
@@ -39,7 +39,7 @@ describe('Dashboard', () => {
     actions: {
       addSliceToDashboard() {},
       removeSliceFromDashboard() {},
-      postChartFormData() {},
+      triggerQuery() {},
       logEvent() {},
     },
     initMessages: [],
@@ -82,15 +82,15 @@ describe('Dashboard', () => {
       },
     };
 
-    it('should call postChartFormData for all non-exempt slices', () => {
+    it('should call triggerQuery for all non-exempt slices', () => {
       const wrapper = setup({ charts: overrideCharts, slices: overrideSlices });
-      const spy = sinon.spy(props.actions, 'postChartFormData');
+      const spy = sinon.spy(props.actions, 'triggerQuery');
       wrapper.instance().refreshExcept('1001');
       spy.restore();
       expect(spy.callCount).toBe(Object.keys(overrideCharts).length - 1);
     });
 
-    it('should not call postChartFormData for filter_immune_slices', () => {
+    it('should not call triggerQuery for filter_immune_slices', () => {
       const wrapper = setup({
         charts: overrideCharts,
         dashboardInfo: {
@@ -103,7 +103,7 @@ describe('Dashboard', () => {
           },
         },
       });
-      const spy = sinon.spy(props.actions, 'postChartFormData');
+      const spy = sinon.spy(props.actions, 'triggerQuery');
       wrapper.instance().refreshExcept();
       spy.restore();
       expect(spy.callCount).toBe(0);
diff --git a/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx b/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx
index 842c5e2..cda5e0a 100644
--- a/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx
+++ b/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx
@@ -18,7 +18,8 @@
  */
 import React from 'react';
 import { shallow } from 'enzyme';
-import { getFormDataFromControls, defaultControls } from 'src/explore/store';
+import { defaultControls } from 'src/explore/store';
+import { getFormDataFromControls } from 'src/explore/controlUtils';
 import { ControlPanelsContainer } from 'src/explore/components/ControlPanelsContainer';
 import ControlPanelSection from 'src/explore/components/ControlPanelSection';
 import * as featureFlags from 'src/featureFlags';
diff --git a/superset/assets/spec/javascripts/explore/components/ExploreViewContainer_spec.jsx b/superset/assets/spec/javascripts/explore/components/ExploreViewContainer_spec.jsx
index ee3a006..bdaa642 100644
--- a/superset/assets/spec/javascripts/explore/components/ExploreViewContainer_spec.jsx
+++ b/superset/assets/spec/javascripts/explore/components/ExploreViewContainer_spec.jsx
@@ -19,6 +19,7 @@
 import React from 'react';
 import configureStore from 'redux-mock-store';
 import thunk from 'redux-thunk';
+import sinon from 'sinon';
 import { shallow } from 'enzyme';
 
 import getInitialState from 'src/explore/reducers/getInitialState';
@@ -58,7 +59,7 @@ describe('ExploreViewContainer', () => {
     wrapper = shallow(<ExploreViewContainer />, {
       context: { store },
       disableLifecycleMethods: true,
-    });
+    }).dive();
   });
 
   it('renders', () => {
@@ -68,14 +69,37 @@ describe('ExploreViewContainer', () => {
   });
 
   it('renders QueryAndSaveButtons', () => {
-    expect(wrapper.dive().find(QueryAndSaveBtns)).toHaveLength(1);
+    expect(wrapper.find(QueryAndSaveBtns)).toHaveLength(1);
   });
 
   it('renders ControlPanelsContainer', () => {
-    expect(wrapper.dive().find(ControlPanelsContainer)).toHaveLength(1);
+    expect(wrapper.find(ControlPanelsContainer)).toHaveLength(1);
   });
 
   it('renders ChartContainer', () => {
-    expect(wrapper.dive().find(ChartContainer)).toHaveLength(1);
+    expect(wrapper.find(ChartContainer)).toHaveLength(1);
+  });
+
+  describe('componentWillReceiveProps()', () => {
+    it('when controls change, should call resetControls', () => {
+      expect(wrapper.instance().props.controls.viz_type.value).toBe('table');
+      const resetControls = sinon.stub(wrapper.instance().props.actions, 'resetControls');
+      const triggerQuery = sinon.stub(wrapper.instance().props.actions, 'triggerQuery');
+
+      // triggers componentWillReceiveProps
+      wrapper.setProps({
+        controls: {
+          viz_type: {
+            value: 'bar',
+          },
+        },
+      });
+      expect(resetControls.callCount).toBe(1);
+      // exploreview container should not force chart run query
+      // it should be controlled by redux state.
+      expect(triggerQuery.callCount).toBe(0);
+      resetControls.reset();
+      triggerQuery.reset();
+    });
   });
 });
diff --git a/superset/assets/spec/javascripts/explore/controlUtils_spec.jsx b/superset/assets/spec/javascripts/explore/controlUtils_spec.jsx
new file mode 100644
index 0000000..50f766a
--- /dev/null
+++ b/superset/assets/spec/javascripts/explore/controlUtils_spec.jsx
@@ -0,0 +1,164 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {
+  getControlConfig,
+  getControlState,
+  getControlKeys,
+  applyMapStateToPropsToControl,
+} from '../../../src/explore/controlUtils';
+
+describe('controlUtils', () => {
+  const state = {
+    datasource: {
+      columns: [
+        'a', 'b', 'c',
+      ],
+      metrics: [
+        { metric_name: 'first' },
+        { metric_name: 'second' },
+      ],
+    },
+  };
+
+  describe('getControlConfig', () => {
+    it('returns a valid spatial controlConfig', () => {
+      const spatialControl = getControlConfig('spatial', 'deck_grid');
+      expect(spatialControl.type).toEqual('SpatialControl');
+      expect(spatialControl.validators).toHaveLength(1);
+    });
+    it('overrides according to vizType', () => {
+      let control = getControlConfig('metric', 'line');
+      expect(control.type).toEqual('MetricsControl');
+      expect(control.validators).toHaveLength(1);
+
+      // deck_polygon overrides and removes validators
+      control = getControlConfig('metric', 'deck_polygon');
+      expect(control.type).toEqual('MetricsControl');
+      expect(control.validators).toHaveLength(0);
+    });
+  });
+
+  describe('getControlKeys', () => {
+
+    window.featureFlags = {
+      SCOPED_FILTER: false,
+    };
+
+    it('gets only strings, even when React components are in conf', () => {
+      const keys = getControlKeys('filter_box');
+      expect(keys.every(k => typeof k === 'string')).toEqual(true);
+      expect(keys).toHaveLength(16);
+    });
+    it('gets the right set of controlKeys for filter_box', () => {
+      const keys = getControlKeys('filter_box');
+      expect(keys.sort()).toEqual([
+        'adhoc_filters',
+        'cache_timeout',
+        'datasource',
+        'date_filter',
+        'druid_time_origin',
+        'filter_configs',
+        'granularity',
+        'instant_filtering',
+        'show_druid_time_granularity',
+        'show_druid_time_origin',
+        'show_sqla_time_column',
+        'show_sqla_time_granularity',
+        'slice_id',
+        'time_range',
+        'url_params',
+        'viz_type',
+      ]);
+    });
+  });
+
+  describe('applyMapStateToPropsToControl,', () => {
+    it('applies state to props as expected', () => {
+      let control = getControlConfig('all_columns', 'table');
+      control = applyMapStateToPropsToControl(control, state);
+      expect(control.options).toEqual(['a', 'b', 'c']);
+    });
+
+    it('removes the mapStateToProps key from the object', () => {
+      let control = getControlConfig('all_columns', 'table');
+      control = applyMapStateToPropsToControl(control, state);
+      expect(control.mapStateToProps).toBe(undefined);
+    });
+
+  });
+
+  describe('getControlState', () => {
+
+    it('to be function free', () => {
+      const control = getControlState('all_columns', 'table', state, ['a']);
+      expect(control.mapStateToProps).toBe(undefined);
+      expect(control.validators).toBe(undefined);
+    });
+
+    it('to fix multi with non-array values', () => {
+      const control = getControlState('all_columns', 'table', state, 'a');
+      expect(control.value).toEqual(['a']);
+    });
+
+    it('removes missing/invalid choice', () => {
+      let control = getControlState('stacked_style', 'area', state, 'stack');
+      expect(control.value).toBe('stack');
+
+      control = getControlState('stacked_style', 'area', state, 'FOO');
+      expect(control.value).toBe(null);
+    });
+
+    it('applies the default function for metrics', () => {
+      const control = getControlState('metrics', 'table', state);
+      expect(control.default).toEqual(['first']);
+    });
+
+    it('applies the default function for metric', () => {
+      const control = getControlState('metric', 'table', state);
+      expect(control.default).toEqual('first');
+    });
+
+    it('applies the default function, prefers count if it exists', () => {
+      const stateWithCount = {
+        ...state,
+        datasource: {
+          ...state.datasource,
+          metrics: [
+            { metric_name: 'first' },
+            { metric_name: 'second' },
+            { metric_name: 'count' },
+          ],
+        },
+      };
+      const control = getControlState('metrics', 'table', stateWithCount);
+      expect(control.default).toEqual(['count']);
+    });
+
+  });
+
+  describe('validateControl', () => {
+
+    it('validates the control, returns an error if empty', () => {
+      const control = getControlState('metric', 'table', state, null);
+      expect(control.validationErrors).toEqual(['cannot be empty']);
+    });
+
+  });
+
+});
diff --git a/superset/assets/spec/javascripts/explore/store_spec.jsx b/superset/assets/spec/javascripts/explore/store_spec.jsx
new file mode 100644
index 0000000..4884ed1
--- /dev/null
+++ b/superset/assets/spec/javascripts/explore/store_spec.jsx
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { applyDefaultFormData } from '../../../src/explore/store';
+
+describe('store', () => {
+
+  describe('applyDefaultFormData', () => {
+
+    window.featureFlags = {
+      SCOPED_FILTER: false,
+    };
+
+    it('applies default to formData if the key is missing', () => {
+      const inputFormData = {
+        datasource: '11_table',
+        viz_type: 'table',
+      };
+      let outputFormData = applyDefaultFormData(inputFormData);
+      expect(outputFormData.row_limit).toEqual(10000);
+
+      const inputWithRowLimit = {
+        ...inputFormData,
+        row_limit: 888,
+      };
+      outputFormData = applyDefaultFormData(inputWithRowLimit);
+      expect(outputFormData.row_limit).toEqual(888);
+    });
+
+    it('keeps null if key is defined with null', () => {
+      const inputFormData = {
+        datasource: '11_table',
+        viz_type: 'table',
+        row_limit: null,
+      };
+      const outputFormData = applyDefaultFormData(inputFormData);
+      expect(outputFormData.row_limit).toBe(null);
+    });
+
+    it('removes out of scope, or deprecated keys', () => {
+      const inputFormData = {
+        datasource: '11_table',
+        viz_type: 'table',
+        this_should_no_be_here: true,
+      };
+      const outputFormData = applyDefaultFormData(inputFormData);
+      expect(outputFormData.this_should_no_be_here).toBe(undefined);
+    });
+
+  });
+});
diff --git a/superset/assets/src/SqlLab/actions/sqlLab.js b/superset/assets/src/SqlLab/actions/sqlLab.js
index b8e785a..81c8e8d 100644
--- a/superset/assets/src/SqlLab/actions/sqlLab.js
+++ b/superset/assets/src/SqlLab/actions/sqlLab.js
@@ -339,7 +339,8 @@ export function addTable(query, tableName, schemaName) {
       }),
     );
 
-    SupersetClient.get({ endpoint: `/superset/table/${query.dbId}/${tableName}/${schemaName}/` })
+    SupersetClient.get({ endpoint: encodeURI(`/superset/table/${query.dbId}/` +
+            `${encodeURIComponent(tableName)}/${encodeURIComponent(schemaName)}/`) })
       .then(({ json }) => {
         const dataPreviewQuery = {
           id: shortid.generate(),
@@ -376,7 +377,8 @@ export function addTable(query, tableName, schemaName) {
       );
 
     SupersetClient.get({
-      endpoint: `/superset/extra_table_metadata/${query.dbId}/${tableName}/${schemaName}/`,
+      endpoint: encodeURI(`/superset/extra_table_metadata/${query.dbId}/` +
+          `${encodeURIComponent(tableName)}/${encodeURIComponent(schemaName)}/`),
     })
       .then(({ json }) =>
         dispatch(mergeTable({ ...table, ...json, isExtraMetadataLoading: false })),
diff --git a/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx b/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
index 13834cb..8ad02ad 100644
--- a/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
+++ b/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
@@ -27,7 +27,7 @@ import * as Actions from '../actions/sqlLab';
 const QUERY_UPDATE_FREQ = 2000;
 const QUERY_UPDATE_BUFFER_MS = 5000;
 const MAX_QUERY_AGE_TO_POLL = 21600000;
-const QUERY_TIMEOUT_LIMIT = 7000;
+const QUERY_TIMEOUT_LIMIT = 10000;
 
 class QueryAutoRefresh extends React.PureComponent {
   componentWillMount() {
diff --git a/superset/assets/src/chart/Chart.jsx b/superset/assets/src/chart/Chart.jsx
index cfc6ce8..0218a17 100644
--- a/superset/assets/src/chart/Chart.jsx
+++ b/superset/assets/src/chart/Chart.jsx
@@ -22,6 +22,7 @@ import { Alert } from 'react-bootstrap';
 
 import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
 import { Logger, LOG_ACTIONS_RENDER_CHART_CONTAINER } from '../logger/LogUtils';
+import { safeStringify } from '../utils/safeStringify';
 import Loading from '../components/Loading';
 import RefreshChartOverlay from '../components/RefreshChartOverlay';
 import StackTraceMessage from '../components/StackTraceMessage';
@@ -69,25 +70,38 @@ class Chart extends React.PureComponent {
     super(props);
     this.handleRenderContainerFailure = this.handleRenderContainerFailure.bind(this);
   }
+
   componentDidMount() {
     if (this.props.triggerQuery) {
-      if (this.props.chartId > 0 && isFeatureEnabled(FeatureFlag.CLIENT_CACHE)) {
-        // Load saved chart with a GET request
-        this.props.actions.getSavedChart(
-          this.props.formData,
-          false,
-          this.props.timeout,
-          this.props.chartId,
-        );
-      } else {
-        // Create chart with POST request
-        this.props.actions.postChartFormData(
-          this.props.formData,
-          false,
-          this.props.timeout,
-          this.props.chartId,
-        );
-      }
+      this.runQuery();
+    }
+  }
+
+  componentDidUpdate(prevProps) {
+    if (this.props.triggerQuery &&
+      safeStringify(prevProps.formData) !== safeStringify(this.props.formData)
+    ) {
+      this.runQuery();
+    }
+  }
+
+  runQuery() {
+    if (this.props.chartId > 0 && isFeatureEnabled(FeatureFlag.CLIENT_CACHE)) {
+      // Load saved chart with a GET request
+      this.props.actions.getSavedChart(
+        this.props.formData,
+        false,
+        this.props.timeout,
+        this.props.chartId,
+      );
+    } else {
+      // Create chart with POST request
+      this.props.actions.postChartFormData(
+        this.props.formData,
+        false,
+        this.props.timeout,
+        this.props.chartId,
+      );
     }
   }
 
diff --git a/superset/assets/src/components/TableSelector.jsx b/superset/assets/src/components/TableSelector.jsx
index c3e405e..ba2cebb 100644
--- a/superset/assets/src/components/TableSelector.jsx
+++ b/superset/assets/src/components/TableSelector.jsx
@@ -90,7 +90,7 @@ export default class TableSelector extends React.PureComponent {
   onChange() {
     this.props.onChange({
       dbId: this.state.dbId,
-      shema: this.state.schema,
+      schema: this.state.schema,
       tableName: this.state.tableName,
     });
   }
@@ -101,9 +101,8 @@ export default class TableSelector extends React.PureComponent {
       return Promise.resolve({ options });
     }
     return SupersetClient.get({
-      endpoint: (
-        `/superset/tables/${this.props.dbId}/` +
-        `${this.props.schema}/${input}`),
+      endpoint: encodeURI(`/superset/tables/${this.props.dbId}/` +
+        `${encodeURIComponent(this.props.schema)}/${encodeURIComponent(input)}`),
     }).then(({ json }) => ({ options: this.addOptionIfMissing(json.options, tableName) }));
   }
   dbMutator(data) {
@@ -123,7 +122,8 @@ export default class TableSelector extends React.PureComponent {
     const { dbId, schema } = this.props;
     if (dbId && schema) {
       this.setState(() => ({ tableLoading: true, tableOptions: [] }));
-      const endpoint = `/superset/tables/${dbId}/${schema}/${substr}/${forceRefresh}/`;
+      const endpoint = encodeURI(`/superset/tables/${dbId}/` +
+          `${encodeURIComponent(schema)}/${encodeURIComponent(substr)}/${forceRefresh}/`);
        return SupersetClient.get({ endpoint })
         .then(({ json }) => {
           const filterOptions = createFilterOptions({ options: json.options });
diff --git a/superset/assets/src/dashboard/actions/dashboardLayout.js b/superset/assets/src/dashboard/actions/dashboardLayout.js
index b76966c..b276e37 100644
--- a/superset/assets/src/dashboard/actions/dashboardLayout.js
+++ b/superset/assets/src/dashboard/actions/dashboardLayout.js
@@ -17,8 +17,9 @@
  * under the License.
  */
 import { ActionCreators as UndoActionCreators } from 'redux-undo';
+import { t } from '@superset-ui/translation';
 
-import { addInfoToast } from '../../messageToasts/actions';
+import { addWarningToast } from '../../messageToasts/actions';
 import { setUnsavedChanges } from './dashboardState';
 import { TABS_TYPE, ROW_TYPE } from '../util/componentTypes';
 import {
@@ -153,8 +154,10 @@ export function handleComponentDrop(dropResult) {
 
     if (overflowsParent) {
       return dispatch(
-        addInfoToast(
-          `There is not enough space for this component. Try decreasing its width, or increasing the destination width.`,
+        addWarningToast(
+          t(
+            `There is not enough space for this component. Try decreasing its width, or increasing the destination width.`,
+          ),
         ),
       );
     }
@@ -162,12 +165,30 @@ export function handleComponentDrop(dropResult) {
     const { source, destination } = dropResult;
     const droppedOnRoot = destination && destination.id === DASHBOARD_ROOT_ID;
     const isNewComponent = source.id === NEW_COMPONENTS_SOURCE_ID;
+    const dashboardRoot = getState().dashboardLayout.present[DASHBOARD_ROOT_ID];
+    const rootChildId =
+      dashboardRoot && dashboardRoot.children ? dashboardRoot.children[0] : '';
 
     if (droppedOnRoot) {
       dispatch(createTopLevelTabs(dropResult));
     } else if (destination && isNewComponent) {
       dispatch(createComponent(dropResult));
     } else if (
+      // Add additional allow-to-drop logic for tag/tags source.
+      // We only allow
+      // - top-level tab => top-level tab: rearrange top-level tab order
+      // - nested tab => top-level tab: allow row tab become top-level tab
+      // Dashboard does not allow top-level tab become nested tab, to avoid
+      // nested tab inside nested tab.
+      source.type === TABS_TYPE &&
+      destination.type === TABS_TYPE &&
+      source.id === rootChildId &&
+      destination.id !== rootChildId
+    ) {
+      return dispatch(
+        addWarningToast(t(`Can not move top level tab into nested tabs`)),
+      );
+    } else if (
       destination &&
       source &&
       !// ensure it has moved
@@ -176,6 +197,8 @@ export function handleComponentDrop(dropResult) {
       dispatch(moveComponent(dropResult));
     }
 
+    // call getState() again down here in case redux state is stale after
+    // previous dispatch(es)
     const { dashboardLayout: undoableLayout } = getState();
 
     // if we moved a child from a Tab or Row parent and it was the only child, delete the parent.
diff --git a/superset/assets/src/dashboard/components/Dashboard.jsx b/superset/assets/src/dashboard/components/Dashboard.jsx
index b26cde6..b584559 100644
--- a/superset/assets/src/dashboard/components/Dashboard.jsx
+++ b/superset/assets/src/dashboard/components/Dashboard.jsx
@@ -30,7 +30,6 @@ import {
   loadStatsPropShape,
 } from '../util/propShapes';
 import { areObjectsEqual } from '../../reduxUtils';
-import getFormDataWithExtraFilters from '../util/charts/getFormDataWithExtraFilters';
 import { LOG_ACTIONS_MOUNT_DASHBOARD } from '../../logger/LogUtils';
 import OmniContainer from '../../components/OmniContainer';
 
@@ -40,7 +39,7 @@ const propTypes = {
   actions: PropTypes.shape({
     addSliceToDashboard: PropTypes.func.isRequired,
     removeSliceFromDashboard: PropTypes.func.isRequired,
-    postChartFormData: PropTypes.func.isRequired,
+    triggerQuery: PropTypes.func.isRequired,
     logEvent: PropTypes.func.isRequired,
   }).isRequired,
   dashboardInfo: dashboardInfoPropShape.isRequired,
@@ -149,21 +148,7 @@ class Dashboard extends React.PureComponent {
     this.getAllCharts().forEach(chart => {
       // filterKey is a string, immune array contains numbers
       if (String(chart.id) !== filterKey && immune.indexOf(chart.id) === -1) {
-        const updatedFormData = getFormDataWithExtraFilters({
-          chart,
-          dashboardMetadata: this.props.dashboardInfo.metadata,
-          filters: this.props.dashboardState.filters,
-          colorScheme: this.props.dashboardState.colorScheme,
-          colorNamespace: this.props.dashboardState.colorNamespace,
-          sliceId: chart.id,
-        });
-
-        this.props.actions.postChartFormData(
-          updatedFormData,
-          false,
-          this.props.timeout,
-          chart.id,
-        );
+        this.props.actions.triggerQuery(true, chart.id);
       }
     });
   }
diff --git a/superset/assets/src/dashboard/components/DashboardBuilder.jsx b/superset/assets/src/dashboard/components/DashboardBuilder.jsx
index 12c8ff3..eadaab4 100644
--- a/superset/assets/src/dashboard/components/DashboardBuilder.jsx
+++ b/superset/assets/src/dashboard/components/DashboardBuilder.jsx
@@ -84,10 +84,11 @@ class DashboardBuilder extends React.Component {
     const { dashboardLayout, directPathToChild } = props;
     const dashboardRoot = dashboardLayout[DASHBOARD_ROOT_ID];
     const rootChildId = dashboardRoot.children[0];
-    const topLevelTabs =
-      rootChildId !== DASHBOARD_GRID_ID && dashboardLayout[rootChildId];
     const tabIndex = findTabIndexByComponentId({
-      currentComponent: topLevelTabs || dashboardLayout[DASHBOARD_ROOT_ID],
+      currentComponent:
+        rootChildId === DASHBOARD_GRID_ID
+          ? dashboardLayout[DASHBOARD_ROOT_ID]
+          : dashboardLayout[rootChildId],
       directPathToChild,
     });
 
@@ -221,6 +222,7 @@ class DashboardBuilder extends React.Component {
                           // see isValidChild for why tabs do not increment the depth of their children
                           depth={DASHBOARD_ROOT_DEPTH + 1} // (topLevelTabs ? 0 : 1)}
                           width={width}
+                          isComponentVisible={index === tabIndex}
                         />
                       </TabPane>
                     ))}
diff --git a/superset/assets/src/dashboard/components/DashboardGrid.jsx b/superset/assets/src/dashboard/components/DashboardGrid.jsx
index b036ad0..0666f47 100644
--- a/superset/assets/src/dashboard/components/DashboardGrid.jsx
+++ b/superset/assets/src/dashboard/components/DashboardGrid.jsx
@@ -30,6 +30,7 @@ const propTypes = {
   editMode: PropTypes.bool.isRequired,
   gridComponent: componentShape.isRequired,
   handleComponentDrop: PropTypes.func.isRequired,
+  isComponentVisible: PropTypes.bool.isRequired,
   resizeComponent: PropTypes.func.isRequired,
   width: PropTypes.number.isRequired,
 };
@@ -114,6 +115,7 @@ class DashboardGrid extends React.PureComponent {
       depth,
       editMode,
       width,
+      isComponentVisible,
     } = this.props;
 
     const columnPlusGutterWidth =
@@ -154,6 +156,7 @@ class DashboardGrid extends React.PureComponent {
               index={index}
               availableColumnCount={GRID_COLUMN_COUNT}
               columnWidth={columnWidth}
+              isComponentVisible={isComponentVisible}
               onResizeStart={this.handleResizeStart}
               onResize={this.handleResize}
               onResizeStop={this.handleResizeStop}
diff --git a/superset/assets/src/dashboard/components/gridComponents/Chart.jsx b/superset/assets/src/dashboard/components/gridComponents/Chart.jsx
index ff11208..9b1b5f1 100644
--- a/superset/assets/src/dashboard/components/gridComponents/Chart.jsx
+++ b/superset/assets/src/dashboard/components/gridComponents/Chart.jsx
@@ -37,6 +37,7 @@ const propTypes = {
   width: PropTypes.number.isRequired,
   height: PropTypes.number.isRequired,
   updateSliceName: PropTypes.func.isRequired,
+  isComponentVisible: PropTypes.bool,
 
   // from redux
   chart: PropTypes.shape(chartPropShape).isRequired,
@@ -61,6 +62,7 @@ const propTypes = {
 
 const defaultProps = {
   isCached: false,
+  isComponentVisible: true,
 };
 
 // we use state + shouldComponentUpdate() logic to prevent perf-wrecking
@@ -99,19 +101,27 @@ class Chart extends React.Component {
       return true;
     }
 
-    for (let i = 0; i < SHOULD_UPDATE_ON_PROP_CHANGES.length; i += 1) {
-      const prop = SHOULD_UPDATE_ON_PROP_CHANGES[i];
-      if (nextProps[prop] !== this.props[prop]) {
+    // allow chart update/re-render only if visible:
+    // under selected tab or no tab layout
+    if (nextProps.isComponentVisible) {
+      if (nextProps.chart.triggerQuery) {
         return true;
       }
-    }
 
-    if (
-      nextProps.width !== this.props.width ||
-      nextProps.height !== this.props.height
-    ) {
-      clearTimeout(this.resizeTimeout);
-      this.resizeTimeout = setTimeout(this.resize, RESIZE_TIMEOUT);
+      for (let i = 0; i < SHOULD_UPDATE_ON_PROP_CHANGES.length; i += 1) {
+        const prop = SHOULD_UPDATE_ON_PROP_CHANGES[i];
+        if (nextProps[prop] !== this.props[prop]) {
+          return true;
+        }
+      }
+
+      if (
+        nextProps.width !== this.props.width ||
+        nextProps.height !== this.props.height
+      ) {
+        clearTimeout(this.resizeTimeout);
+        this.resizeTimeout = setTimeout(this.resize, RESIZE_TIMEOUT);
+      }
     }
 
     return false;
diff --git a/superset/assets/src/dashboard/components/gridComponents/ChartHolder.jsx b/superset/assets/src/dashboard/components/gridComponents/ChartHolder.jsx
index 836c0e7..706023a 100644
--- a/superset/assets/src/dashboard/components/gridComponents/ChartHolder.jsx
+++ b/superset/assets/src/dashboard/components/gridComponents/ChartHolder.jsx
@@ -109,6 +109,7 @@ class ChartHolder extends React.Component {
       onResizeStop,
       handleComponentDrop,
       editMode,
+      isComponentVisible,
     } = this.props;
 
     // inherit the size of parent columns
@@ -163,6 +164,7 @@ class ChartHolder extends React.Component {
                 )}
                 sliceName={component.meta.sliceName || ''}
                 updateSliceName={this.handleUpdateSliceName}
+                isComponentVisible={isComponentVisible}
               />
               {editMode && (
                 <HoverMenu position="top">
diff --git a/superset/assets/src/dashboard/components/gridComponents/Column.jsx b/superset/assets/src/dashboard/components/gridComponents/Column.jsx
index 7170e4a..78d272b 100644
--- a/superset/assets/src/dashboard/components/gridComponents/Column.jsx
+++ b/superset/assets/src/dashboard/components/gridComponents/Column.jsx
@@ -112,6 +112,7 @@ class Column extends React.PureComponent {
       onResizeStop,
       handleComponentDrop,
       editMode,
+      isComponentVisible,
     } = this.props;
 
     const columnItems = columnComponent.children || [];
@@ -191,6 +192,7 @@ class Column extends React.PureComponent {
                     onResizeStart={onResizeStart}
                     onResize={onResize}
                     onResizeStop={onResizeStop}
+                    isComponentVisible={isComponentVisible}
                   />
                 ))}
 
diff --git a/superset/assets/src/dashboard/components/gridComponents/Row.jsx b/superset/assets/src/dashboard/components/gridComponents/Row.jsx
index 585e57c..f9076bc 100644
--- a/superset/assets/src/dashboard/components/gridComponents/Row.jsx
+++ b/superset/assets/src/dashboard/components/gridComponents/Row.jsx
@@ -113,6 +113,7 @@ class Row extends React.PureComponent {
       onResizeStop,
       handleComponentDrop,
       editMode,
+      isComponentVisible,
     } = this.props;
 
     const rowItems = rowComponent.children || [];
@@ -177,6 +178,7 @@ class Row extends React.PureComponent {
                   onResizeStart={onResizeStart}
                   onResize={onResize}
                   onResizeStop={onResizeStop}
+                  isComponentVisible={isComponentVisible}
                 />
               ))}
 
diff --git a/superset/assets/src/dashboard/components/gridComponents/Tab.jsx b/superset/assets/src/dashboard/components/gridComponents/Tab.jsx
index 49a0f18..e9e543c 100644
--- a/superset/assets/src/dashboard/components/gridComponents/Tab.jsx
+++ b/superset/assets/src/dashboard/components/gridComponents/Tab.jsx
@@ -26,7 +26,6 @@ import AnchorLink from '../../../components/AnchorLink';
 import DeleteComponentModal from '../DeleteComponentModal';
 import WithPopoverMenu from '../menu/WithPopoverMenu';
 import { componentShape } from '../../util/propShapes';
-import { DASHBOARD_ROOT_DEPTH } from '../../util/constants';
 
 export const RENDER_TAB = 'RENDER_TAB';
 export const RENDER_TAB_CONTENT = 'RENDER_TAB_CONTENT';
@@ -134,6 +133,7 @@ export default class Tab extends React.PureComponent {
       onResize,
       onResizeStop,
       editMode,
+      isComponentVisible,
     } = this.props;
 
     return (
@@ -170,6 +170,7 @@ export default class Tab extends React.PureComponent {
             onResizeStart={onResizeStart}
             onResize={onResize}
             onResizeStop={onResizeStop}
+            isComponentVisible={isComponentVisible}
           />
         ))}
         {/* Make bottom of tab droppable */}
@@ -219,10 +220,6 @@ export default class Tab extends React.PureComponent {
         index={index}
         depth={depth}
         onDrop={this.handleDrop}
-        // disable drag drop of top-level Tab's to prevent invalid nesting of a child in
-        // itself, e.g. if a top-level Tab has a Tabs child, dragging the Tab into the Tabs would
-        // reusult in circular children
-        disableDragDrop={depth <= DASHBOARD_ROOT_DEPTH + 1}
         editMode={editMode}
       >
         {({ dropIndicatorProps, dragSourceRef }) => (
diff --git a/superset/assets/src/dashboard/components/gridComponents/Tabs.jsx b/superset/assets/src/dashboard/components/gridComponents/Tabs.jsx
index 2b8934e..dfa0cae 100644
--- a/superset/assets/src/dashboard/components/gridComponents/Tabs.jsx
+++ b/superset/assets/src/dashboard/components/gridComponents/Tabs.jsx
@@ -238,6 +238,7 @@ class Tabs extends React.PureComponent {
                       onResize={onResize}
                       onResizeStop={onResizeStop}
                       onDropOnTab={this.handleDropOnTab}
+                      isComponentVisible={selectedTabIndex === tabIndex}
                     />
                   )}
                 </BootstrapTab>
diff --git a/superset/assets/src/dashboard/containers/Dashboard.jsx b/superset/assets/src/dashboard/containers/Dashboard.jsx
index e5cf4fb..c1609a9 100644
--- a/superset/assets/src/dashboard/containers/Dashboard.jsx
+++ b/superset/assets/src/dashboard/containers/Dashboard.jsx
@@ -25,7 +25,7 @@ import {
   addSliceToDashboard,
   removeSliceFromDashboard,
 } from '../actions/dashboardState';
-import { postChartFormData } from '../../chart/chartAction';
+import { triggerQuery } from '../../chart/chartAction';
 import { logEvent } from '../../logger/actions';
 import getLoadStatsPerTopLevelComponent from '../util/logging/getLoadStatsPerTopLevelComponent';
 
@@ -64,7 +64,7 @@ function mapDispatchToProps(dispatch) {
       {
         addSliceToDashboard,
         removeSliceFromDashboard,
-        postChartFormData,
+        triggerQuery,
         logEvent,
       },
       dispatch,
diff --git a/superset/assets/src/dashboard/containers/DashboardComponent.jsx b/superset/assets/src/dashboard/containers/DashboardComponent.jsx
index a1a1c37..2bd3060 100644
--- a/superset/assets/src/dashboard/containers/DashboardComponent.jsx
+++ b/superset/assets/src/dashboard/containers/DashboardComponent.jsx
@@ -48,6 +48,7 @@ const propTypes = {
 
 const defaultProps = {
   directPathToChild: [],
+  isComponentVisible: true,
 };
 
 function mapStateToProps(
diff --git a/superset/assets/src/explore/components/ExploreViewContainer.jsx b/superset/assets/src/explore/components/ExploreViewContainer.jsx
index 431f252..56b5e43 100644
--- a/superset/assets/src/explore/components/ExploreViewContainer.jsx
+++ b/superset/assets/src/explore/components/ExploreViewContainer.jsx
@@ -29,7 +29,7 @@ import SaveModal from './SaveModal';
 import QueryAndSaveBtns from './QueryAndSaveBtns';
 import { getExploreUrlAndPayload, getExploreLongUrl } from '../exploreUtils';
 import { areObjectsEqual } from '../../reduxUtils';
-import { getFormDataFromControls } from '../store';
+import { getFormDataFromControls } from '../controlUtils';
 import { chartPropShape } from '../../dashboard/util/propShapes';
 import * as exploreActions from '../actions/exploreActions';
 import * as saveModalActions from '../actions/saveModalActions';
@@ -106,7 +106,6 @@ class ExploreViewContainer extends React.Component {
   componentWillReceiveProps(nextProps) {
     if (nextProps.controls.viz_type.value !== this.props.controls.viz_type.value) {
       this.props.actions.resetControls();
-      this.props.actions.triggerQuery(true, this.props.chart.id);
     }
     if (
       nextProps.controls.datasource &&
diff --git a/superset/assets/src/explore/controlUtils.js b/superset/assets/src/explore/controlUtils.js
new file mode 100644
index 0000000..65e8c47
--- /dev/null
+++ b/superset/assets/src/explore/controlUtils.js
@@ -0,0 +1,124 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import controlPanelConfigs, { sectionsToRender } from './controlPanels';
+import controls from './controls';
+
+export function getFormDataFromControls(controlsState) {
+  const formData = {};
+  Object.keys(controlsState).forEach((controlName) => {
+    formData[controlName] = controlsState[controlName].value;
+  });
+  return formData;
+}
+
+export function validateControl(control) {
+  const validators = control.validators;
+  if (validators && validators.length > 0) {
+    const validatedControl = { ...control };
+    const validationErrors = [];
+    validators.forEach((f) => {
+      const v = f(control.value);
+      if (v) {
+        validationErrors.push(v);
+      }
+    });
+    delete validatedControl.validators;
+    return { ...validatedControl, validationErrors };
+  }
+  return control;
+}
+
+export function getControlKeys(vizType, datasourceType) {
+  const controlKeys = [];
+  sectionsToRender(vizType, datasourceType).forEach(
+    section => section.controlSetRows.forEach(
+      fieldsetRow => fieldsetRow.forEach(
+        (field) => {
+          if (typeof field === 'string') {
+            controlKeys.push(field);
+          }
+        })));
+  return controlKeys;
+}
+
+export function getControlConfig(controlKey, vizType) {
+  // Gets the control definition, applies overrides, and executes
+  // the mapStatetoProps
+  const vizConf = controlPanelConfigs[vizType] || {};
+  const controlOverrides = vizConf.controlOverrides || {};
+  const control = {
+    ...controls[controlKey],
+    ...controlOverrides[controlKey],
+  };
+  return control;
+}
+
+export function applyMapStateToPropsToControl(control, state) {
+  if (control.mapStateToProps) {
+    const appliedControl = { ...control };
+    if (state) {
+      Object.assign(appliedControl, control.mapStateToProps(state, control));
+    }
+    delete appliedControl.mapStateToProps;
+    return appliedControl;
+  }
+  return control;
+}
+
+function handleMissingChoice(controlKey, control) {
+  // If the value is not valid anymore based on choices, clear it
+  const value = control.value;
+  if (
+    control.type === 'SelectControl' &&
+    !control.freeForm &&
+    control.choices &&
+    value
+  ) {
+    const alteredControl = { ...control };
+    const choiceValues = control.choices.map(c => c[0]);
+    if (control.multi && value.length > 0) {
+      alteredControl.value = value.filter(el => choiceValues.indexOf(el) > -1);
+      return alteredControl;
+    } else if (!control.multi && choiceValues.indexOf(value) < 0) {
+      alteredControl.value = null;
+      return alteredControl;
+    }
+  }
+  return control;
+}
+
+export function getControlState(controlKey, vizType, state, value) {
+  let controlValue = value;
+  const controlConfig = getControlConfig(controlKey, vizType);
+  let controlState = { ...controlConfig };
+  controlState = applyMapStateToPropsToControl(controlState, state);
+
+  // If default is a function, evaluate it
+  if (typeof controlState.default === 'function') {
+    controlState.default = controlState.default(controlState);
+  }
+
+  // If a choice control went from multi=false to true, wrap value in array
+  if (controlConfig.multi && value && !Array.isArray(value)) {
+    controlValue = [value];
+  }
+  controlState.value = controlValue === undefined ? controlState.default : controlValue;
+  controlState = handleMissingChoice(controlKey, controlState);
+  return validateControl(controlState);
+}
diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx
index 95e2b64..3c239ab 100644
--- a/superset/assets/src/explore/controls.jsx
+++ b/superset/assets/src/explore/controls.jsx
@@ -2095,6 +2095,7 @@ export const controls = {
     type: 'ColorMapControl',
     label: t('Color Map'),
     default: {},
+    renderTrigger: true,
     mapStateToProps: state => ({
       colorNamespace: state.form_data.color_namespace,
       colorScheme: state.form_data.color_scheme,
diff --git a/superset/assets/src/explore/reducers/exploreReducer.js b/superset/assets/src/explore/reducers/exploreReducer.js
index 6f6a1a9..44d5a72 100644
--- a/superset/assets/src/explore/reducers/exploreReducer.js
+++ b/superset/assets/src/explore/reducers/exploreReducer.js
@@ -17,8 +17,10 @@
  * under the License.
  */
 /* eslint camelcase: 0 */
-import { validateControl, getControlsState, getFormDataFromControls } from '../store';
-import controls from '../controls';
+import {
+  getControlsState,
+} from '../store';
+import { getControlState, getFormDataFromControls } from '../controlUtils';
 import * as actions from '../actions/exploreActions';
 
 export default function exploreReducer(state = {}, action) {
@@ -78,11 +80,10 @@ export default function exploreReducer(state = {}, action) {
     [actions.SET_FIELD_VALUE]() {
       // These errors are reported from the Control components
       let errors = action.validationErrors || [];
-      let control = {
-        ...controls[action.controlName],
-        value: action.value,
+      const vizType = state.form_data.viz_type;
+      const control = {
+        ...getControlState(action.controlName, vizType, state, action.value),
       };
-      control = validateControl(control);
 
       // These errors are based on control config `validators`
       errors = errors.concat(control.validationErrors || []);
diff --git a/superset/assets/src/explore/reducers/getInitialState.js b/superset/assets/src/explore/reducers/getInitialState.js
index 98b9799..892224d 100644
--- a/superset/assets/src/explore/reducers/getInitialState.js
+++ b/superset/assets/src/explore/reducers/getInitialState.js
@@ -20,7 +20,8 @@ import shortid from 'shortid';
 
 import getToastsFromPyFlashMessages from '../../messageToasts/utils/getToastsFromPyFlashMessages';
 import { getChartKey } from '../exploreUtils';
-import { getControlsState, getFormDataFromControls } from '../store';
+import { getControlsState } from '../store';
+import { getFormDataFromControls } from '../controlUtils';
 
 export default function getInitialState(bootstrapData) {
   const controls = getControlsState(bootstrapData, bootstrapData.form_data);
diff --git a/superset/assets/src/explore/store.js b/superset/assets/src/explore/store.js
index df456c2..6d58b8d 100644
--- a/superset/assets/src/explore/store.js
+++ b/superset/assets/src/explore/store.js
@@ -17,44 +17,13 @@
  * under the License.
  */
 /* eslint camelcase: 0 */
-import React from 'react';
+import {
+  getControlState,
+  getControlKeys,
+  getFormDataFromControls,
+} from './controlUtils';
 import controls from './controls';
-import controlPanelConfigs, { sectionsToRender } from './controlPanels';
-
-export function getFormDataFromControls(controlsState) {
-  const formData = {};
-  Object.keys(controlsState).forEach((controlName) => {
-    formData[controlName] = controlsState[controlName].value;
-  });
-  return formData;
-}
-
-export function validateControl(control) {
-  const validators = control.validators;
-  const validationErrors = [];
-  if (validators && validators.length > 0) {
-    validators.forEach((f) => {
-      const v = f(control.value);
-      if (v) {
-        validationErrors.push(v);
-      }
-    });
-  }
-  if (validationErrors.length > 0) {
-    return { ...control, validationErrors };
-  }
-  return control;
-}
-
-
-export function getControlNames(vizType, datasourceType) {
-  const controlNames = [];
-  sectionsToRender(vizType, datasourceType).forEach(
-    section => section.controlSetRows.forEach(
-      fsr => fsr.forEach(
-        f => controlNames.push(f))));
-  return controlNames;
-}
+import controlPanelConfigs from './controlPanels';
 
 function handleDeprecatedControls(formData) {
   // Reacffectation / handling of deprecated controls
@@ -66,104 +35,54 @@ function handleDeprecatedControls(formData) {
   }
 }
 
-export function getControlsState(state, form_data) {
+export function getControlsState(state, inputFormData) {
   /*
   * Gets a new controls object to put in the state. The controls object
   * is similar to the configuration control with only the controls
   * related to the current viz_type, materializes mapStateToProps functions,
-  * adds value keys coming from form_data passed here. This can't be an action creator
+  * adds value keys coming from inputFormData passed here. This can't be an action creator
   * just yet because it's used in both the explore and dashboard views.
   * */
 
   // Getting a list of active control names for the current viz
-  const formData = Object.assign({}, form_data);
+  const formData = Object.assign({}, inputFormData);
   const vizType = formData.viz_type || 'table';
 
   handleDeprecatedControls(formData);
 
-  const controlNames = getControlNames(vizType, state.datasource.type);
+  const controlNames = getControlKeys(vizType, state.datasource.type);
 
   const viz = controlPanelConfigs[vizType] || {};
-  const controlOverrides = viz.controlOverrides || {};
   const controlsState = {};
-  controlNames.forEach((k) => {
-    if (React.isValidElement(k)) {
-      // no state
-      return;
-    }
-    const control = Object.assign({}, controls[k], controlOverrides[k]);
-    if (control.mapStateToProps) {
-      Object.assign(control, control.mapStateToProps(state, control));
-      delete control.mapStateToProps;
-    }
-
-    formData[k] = (control.multi && formData[k] && !Array.isArray(formData[k])) ? [formData[k]]
-      : formData[k];
-
-    // If the value is not valid anymore based on choices, clear it
-    if (
-      control.type === 'SelectControl' &&
-      !control.freeForm &&
-      control.choices &&
-      k !== 'datasource' &&
-      formData[k]
-    ) {
-      const choiceValues = control.choices.map(c => c[0]);
-      if (control.multi && formData[k].length > 0) {
-        formData[k] = formData[k].filter(el => choiceValues.indexOf(el) > -1);
-      } else if (!control.multi && choiceValues.indexOf(formData[k]) < 0) {
-        delete formData[k];
-      }
-    }
 
-    if (typeof control.default === 'function') {
-      control.default = control.default(control);
-    }
-    control.validationErrors = [];
-    control.value = control.default;
-    // formData[k]'s type should match control value type
-    if (formData[k] !== undefined &&
-      (Array.isArray(formData[k]) && control.multi || !control.multi)
-    ) {
-      control.value = formData[k];
-    }
-    controlsState[k] = validateControl(control);
+  controlNames.forEach((k) => {
+    const control = getControlState(k, vizType, state, formData[k]);
+    controlsState[k] = control;
+    formData[k] = control.value;
   });
+
   if (viz.onInit) {
     return viz.onInit(controlsState);
   }
   return controlsState;
 }
 
-export function applyDefaultFormData(form_data) {
-  const datasourceType = form_data.datasource.split('__')[1];
-  const vizType = form_data.viz_type || 'table';
-  const viz = controlPanelConfigs[vizType] || {};
-  const controlNames = getControlNames(vizType, datasourceType);
-  const controlOverrides = viz.controlOverrides || {};
+export function applyDefaultFormData(inputFormData) {
+  const datasourceType = inputFormData.datasource.split('__')[1];
+  const vizType = inputFormData.viz_type;
+  const controlNames = getControlKeys(vizType, datasourceType);
   const formData = {};
   controlNames.forEach((k) => {
-    const control = Object.assign({}, controls[k]);
-    if (controlOverrides[k]) {
-      Object.assign(control, controlOverrides[k]);
-    }
-    if (form_data[k] === undefined) {
-      if (typeof control.default === 'function') {
-        formData[k] = control.default(controls[k]);
-      } else {
-        formData[k] = control.default;
-      }
+    const controlState = getControlState(k, vizType, null, inputFormData[k]);
+    if (inputFormData[k] === undefined) {
+      formData[k] = controlState.value;
     } else {
-      formData[k] = form_data[k];
+      formData[k] = inputFormData[k];
     }
   });
   return formData;
 }
 
-export const autoQueryControls = [
-  'datasource',
-  'viz_type',
-];
 
 const defaultControls = Object.assign({}, controls);
 Object.keys(controls).forEach((f) => {
diff --git a/superset/assets/src/featureFlags.ts b/superset/assets/src/featureFlags.ts
index bd0855e..450ad2c 100644
--- a/superset/assets/src/featureFlags.ts
+++ b/superset/assets/src/featureFlags.ts
@@ -23,7 +23,6 @@ export enum FeatureFlag {
   OMNIBAR = 'OMNIBAR',
   CLIENT_CACHE = 'CLIENT_CACHE',
   SCHEDULED_QUERIES = 'SCHEDULED_QUERIES',
-  SQL_VALIDATORS_BY_ENGINE = 'SQL_VALIDATORS_BY_ENGINE',
 }
 
 export type FeatureFlagMap = {
diff --git a/superset/assets/src/visualizations/deckgl/utils.js b/superset/assets/src/visualizations/deckgl/utils.js
index b2b130a..62024ef 100644
--- a/superset/assets/src/visualizations/deckgl/utils.js
+++ b/superset/assets/src/visualizations/deckgl/utils.js
@@ -34,6 +34,9 @@ export function getBreakPoints({
     // compute evenly distributed break points based on number of buckets
     const numBuckets = formDataNumBuckets ? parseInt(formDataNumBuckets, 10) : DEFAULT_NUM_BUCKETS;
     const [minValue, maxValue] = extent(features, accessor);
+    if (minValue === undefined) {
+      return [];
+    }
     const delta = (maxValue - minValue) / numBuckets;
     const precision = delta === 0
       ? 0
diff --git a/superset/cli.py b/superset/cli.py
index 7f5fe17..5242910 100755
--- a/superset/cli.py
+++ b/superset/cli.py
@@ -24,11 +24,10 @@ from sys import stdout
 import click
 from colorama import Fore, Style
 from pathlib2 import Path
-import werkzeug.serving
 import yaml
 
 from superset import (
-    app, data, db, security_manager,
+    app, appbuilder, data, db, security_manager,
 )
 from superset.utils import (
     core as utils, dashboard_import_export, dict_import_export)
@@ -50,100 +49,10 @@ def make_shell_context():
 def init():
     """Inits the Superset application"""
     utils.get_or_create_main_db()
+    appbuilder.add_permissions(update_perms=True)
     security_manager.sync_role_definitions()
 
 
-def debug_run(app, port, use_reloader):
-    click.secho(
-        '[DEPRECATED] As of Flask >=1.0.0, this command is no longer '
-        'supported, please use `flask run` instead, as documented in our '
-        'CONTRIBUTING.md',
-        fg='red',
-    )
-    click.secho('[example]', fg='yellow')
-    click.secho(
-        'flask run -p 8080 --with-threads --reload --debugger',
-        fg='green',
-    )
-
-
-def console_log_run(app, port, use_reloader):
-    from console_log import ConsoleLog
-    from gevent import pywsgi
-    from geventwebsocket.handler import WebSocketHandler
-
-    app.wsgi_app = ConsoleLog(app.wsgi_app, app.logger)
-
-    def run():
-        server = pywsgi.WSGIServer(
-            ('0.0.0.0', int(port)),
-            app,
-            handler_class=WebSocketHandler)
-        server.serve_forever()
-
-    if use_reloader:
-        from gevent import monkey
-        monkey.patch_all()
-        run = werkzeug.serving.run_with_reloader(run)
-
-    run()
-
-
-@app.cli.command()
-@click.option('--debug', '-d', is_flag=True, help='Start the web server in debug mode')
-@click.option('--console-log', is_flag=True,
-              help='Create logger that logs to the browser console (implies -d)')
-@click.option('--no-reload', '-n', 'use_reloader', flag_value=False,
-              default=config.get('FLASK_USE_RELOAD'),
-              help='Don\'t use the reloader in debug mode')
-@click.option('--address', '-a', default=config.get('SUPERSET_WEBSERVER_ADDRESS'),
-              help='Specify the address to which to bind the web server')
-@click.option('--port', '-p', default=config.get('SUPERSET_WEBSERVER_PORT'),
-              help='Specify the port on which to run the web server')
-@click.option('--workers', '-w', default=config.get('SUPERSET_WORKERS', 2),
-              help='Number of gunicorn web server workers to fire up [DEPRECATED]')
-@click.option('--timeout', '-t', default=config.get('SUPERSET_WEBSERVER_TIMEOUT'),
-              help='Specify the timeout (seconds) for the '
-                   'gunicorn web server [DEPRECATED]')
-@click.option('--socket', '-s', default=config.get('SUPERSET_WEBSERVER_SOCKET'),
-              help='Path to a UNIX socket as an alternative to address:port, e.g. '
-                   '/var/run/superset.sock. '
-                   'Will override the address and port values. [DEPRECATED]')
-def runserver(debug, console_log, use_reloader, address, port, timeout, workers, socket):
-    """Starts a Superset web server."""
-    debug = debug or config.get('DEBUG') or console_log
-    if debug:
-        print(Fore.BLUE + '-=' * 20)
-        print(
-            Fore.YELLOW + 'Starting Superset server in ' +
-            Fore.RED + 'DEBUG' +
-            Fore.YELLOW + ' mode')
-        print(Fore.BLUE + '-=' * 20)
-        print(Style.RESET_ALL)
-        if console_log:
-            console_log_run(app, port, use_reloader)
-        else:
-            debug_run(app, port, use_reloader)
-    else:
-        logging.info(
-            "The Gunicorn 'superset runserver' command is deprecated. Please "
-            "use the 'gunicorn' command instead.")
-        addr_str = f' unix:{socket} ' if socket else f' {address}:{port} '
-        cmd = (
-            'gunicorn '
-            f'-w {workers} '
-            f'--timeout {timeout} '
-            f'-b {addr_str} '
-            '--limit-request-line 0 '
-            '--limit-request-field_size 0 '
-            'superset:app'
-        )
-        print(Fore.GREEN + 'Starting server with command: ')
-        print(Fore.YELLOW + cmd)
-        print(Style.RESET_ALL)
-        Popen(cmd, shell=True).wait()
-
-
 @app.cli.command()
 @click.option('--verbose', '-v', is_flag=True, help='Show extra information')
 def version(verbose):
@@ -208,6 +117,9 @@ def load_examples_run(load_test_data):
         print('Loading DECK.gl demo')
         data.load_deck_dash()
 
+    print('Loading [Tabbed dashboard]')
+    data.load_tabbed_dashboard()
+
 
 @app.cli.command()
 @click.option('--load-test-data', '-t', is_flag=True, help='Load additional test data')
diff --git a/superset/connectors/druid/models.py b/superset/connectors/druid/models.py
index 43a092c..17ec4b8 100644
--- a/superset/connectors/druid/models.py
+++ b/superset/connectors/druid/models.py
@@ -1118,7 +1118,8 @@ class DruidDatasource(Model, BaseDatasource):
             columns.append('__time')
             del qry['post_aggregations']
             del qry['aggregations']
-            qry['dimensions'] = columns
+            del qry['dimensions']
+            qry['columns'] = columns
             qry['metrics'] = []
             qry['granularity'] = 'all'
             qry['limit'] = row_limit
diff --git a/superset/data/__init__.py b/superset/data/__init__.py
index 5090eff..b36a300 100644
--- a/superset/data/__init__.py
+++ b/superset/data/__init__.py
@@ -28,5 +28,6 @@ from .multiformat_time_series import load_multiformat_time_series  # noqa
 from .paris import load_paris_iris_geojson  # noqa
 from .random_time_series import load_random_time_series_data  # noqa
 from .sf_population_polygons import load_sf_population_polygons  # noqa
+from .tabbed_dashboard import load_tabbed_dashboard # noqa
 from .unicode_test_data import load_unicode_test_data  # noqa
 from .world_bank import load_world_bank_health_n_pop  # noqa
diff --git a/superset/data/tabbed_dashboard.py b/superset/data/tabbed_dashboard.py
new file mode 100644
index 0000000..4c81f85
--- /dev/null
+++ b/superset/data/tabbed_dashboard.py
@@ -0,0 +1,324 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Loads datasets, dashboards and slices in a new superset instance"""
+# pylint: disable=C,R,W
+import json
+import os
+import textwrap
+
+import pandas as pd
+from sqlalchemy import DateTime, String
+
+from superset import db
+from superset.connectors.sqla.models import SqlMetric
+from superset.utils import core as utils
+from .helpers import (
+    config,
+    Dash,
+    DATA_FOLDER,
+    get_example_data,
+    get_slice_json,
+    merge_slice,
+    misc_dash_slices,
+    Slice,
+    TBL,
+    update_slice_ids,
+)
+
+
+def load_tabbed_dashboard():
+    """Creating a tabbed dashboard"""
+
+    print("Creating a dashboard with nested tabs")
+    slug = 'tabbed_dash'
+    dash = db.session.query(Dash).filter_by(slug=slug).first()
+
+    if not dash:
+        dash = Dash()
+
+    # reuse charts in "World's Bank Data and create
+    # new dashboard with nested tabs
+    tabbed_dash_slices = set()
+    tabbed_dash_slices.add('Region Filter')
+    tabbed_dash_slices.add('Growth Rate')
+    tabbed_dash_slices.add('Treemap')
+    tabbed_dash_slices.add('Box plot')
+
+    js = textwrap.dedent("""\
+    {
+      "CHART-c0EjR-OZ0n": {
+        "children": [],
+        "id": "CHART-c0EjR-OZ0n",
+        "meta": {
+          "chartId": 870,
+          "height": 50,
+          "sliceName": "Box plot",
+          "width": 4
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS",
+          "ROW-7G2o5uDvfo"
+        ],
+        "type": "CHART"
+      },
+      "CHART-dxV7Il74hH": {
+        "children": [],
+        "id": "CHART-dxV7Il74hH",
+        "meta": {
+          "chartId": 797,
+          "height": 50,
+          "sliceName": "Treemap",
+          "width": 4
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-gcQJxApOZS",
+          "ROW-3PphCz4GD"
+        ],
+        "type": "CHART"
+      },
+      "CHART-jJ5Yj1Ptaz": {
+        "children": [],
+        "id": "CHART-jJ5Yj1Ptaz",
+        "meta": {
+          "chartId": 789,
+          "height": 50,
+          "sliceName": "World's Population",
+          "width": 4
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS",
+          "TABS-CSjo6VfNrj",
+          "TAB-z81Q87PD7",
+          "ROW-G73z9PIHn"
+        ],
+        "type": "CHART"
+      },
+      "CHART-z4gmEuCqQ5": {
+        "children": [],
+        "id": "CHART-z4gmEuCqQ5",
+        "meta": {
+          "chartId": 788,
+          "height": 50,
+          "sliceName": "Region Filter",
+          "width": 4
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS",
+          "TABS-CSjo6VfNrj",
+          "TAB-EcNm_wh922",
+          "ROW-LCjsdSetJ"
+        ],
+        "type": "CHART"
+      },
+      "DASHBOARD_VERSION_KEY": "v2",
+      "GRID_ID": {
+        "children": [],
+        "id": "GRID_ID",
+        "type": "GRID"
+      },
+      "HEADER_ID": {
+        "id": "HEADER_ID",
+        "meta": {
+          "text": "Tabbed Dashboard"
+        },
+        "type": "HEADER"
+      },
+      "ROOT_ID": {
+        "children": [
+          "TABS-lV0r00f4H1"
+        ],
+        "id": "ROOT_ID",
+        "type": "ROOT"
+      },
+      "ROW-3PphCz4GD": {
+        "children": [
+          "CHART-dxV7Il74hH"
+        ],
+        "id": "ROW-3PphCz4GD",
+        "meta": {
+          "background": "BACKGROUND_TRANSPARENT"
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-gcQJxApOZS"
+        ],
+        "type": "ROW"
+      },
+      "ROW-7G2o5uDvfo": {
+        "children": [
+          "CHART-c0EjR-OZ0n"
+        ],
+        "id": "ROW-7G2o5uDvfo",
+        "meta": {
+          "background": "BACKGROUND_TRANSPARENT"
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS"
+        ],
+        "type": "ROW"
+      },
+      "ROW-G73z9PIHn": {
+        "children": [
+          "CHART-jJ5Yj1Ptaz"
+        ],
+        "id": "ROW-G73z9PIHn",
+        "meta": {
+          "background": "BACKGROUND_TRANSPARENT"
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS",
+          "TABS-CSjo6VfNrj",
+          "TAB-z81Q87PD7"
+        ],
+        "type": "ROW"
+      },
+      "ROW-LCjsdSetJ": {
+        "children": [
+          "CHART-z4gmEuCqQ5"
+        ],
+        "id": "ROW-LCjsdSetJ",
+        "meta": {
+          "background": "BACKGROUND_TRANSPARENT"
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS",
+          "TABS-CSjo6VfNrj",
+          "TAB-EcNm_wh922"
+        ],
+        "type": "ROW"
+      },
+      "TAB-EcNm_wh922": {
+        "children": [
+          "ROW-LCjsdSetJ"
+        ],
+        "id": "TAB-EcNm_wh922",
+        "meta": {
+          "text": "row tab 1"
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS",
+          "TABS-CSjo6VfNrj"
+        ],
+        "type": "TAB"
+      },
+      "TAB-NF3dlrWGS": {
+        "children": [
+          "ROW-7G2o5uDvfo",
+          "TABS-CSjo6VfNrj"
+        ],
+        "id": "TAB-NF3dlrWGS",
+        "meta": {
+          "text": "Tab A"
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1"
+        ],
+        "type": "TAB"
+      },
+      "TAB-gcQJxApOZS": {
+        "children": [
+          "ROW-3PphCz4GD"
+        ],
+        "id": "TAB-gcQJxApOZS",
+        "meta": {
+          "text": "Tab B"
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1"
+        ],
+        "type": "TAB"
+      },
+      "TAB-z81Q87PD7": {
+        "children": [
+          "ROW-G73z9PIHn"
+        ],
+        "id": "TAB-z81Q87PD7",
+        "meta": {
+          "text": "row tab 2"
+        },
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS",
+          "TABS-CSjo6VfNrj"
+        ],
+        "type": "TAB"
+      },
+      "TABS-CSjo6VfNrj": {
+        "children": [
+          "TAB-EcNm_wh922",
+          "TAB-z81Q87PD7"
+        ],
+        "id": "TABS-CSjo6VfNrj",
+        "meta": {},
+        "parents": [
+          "ROOT_ID",
+          "TABS-lV0r00f4H1",
+          "TAB-NF3dlrWGS"
+        ],
+        "type": "TABS"
+      },
+      "TABS-lV0r00f4H1": {
+        "children": [
+          "TAB-NF3dlrWGS",
+          "TAB-gcQJxApOZS"
+        ],
+        "id": "TABS-lV0r00f4H1",
+        "meta": {},
+        "parents": [
+          "ROOT_ID"
+        ],
+        "type": "TABS"
+      }
+    }
+        """)
+    pos = json.loads(js)
+    slices = [
+        db.session.query(Slice)
+            .filter_by(slice_name=name)
+            .first()
+        for name in tabbed_dash_slices
+    ]
+
+    slices = sorted(slices, key=lambda x: x.id)
+    update_slice_ids(pos, slices)
+    dash.position_json = json.dumps(pos, indent=4)
+    dash.slices = slices
+    dash.dashboard_title = 'Tabbed Dashboard'
+    dash.slug = slug
+
+    db.session.merge(dash)
+    db.session.commit()
diff --git a/superset/data/world_bank.py b/superset/data/world_bank.py
index 16aa0cb..94aa468 100644
--- a/superset/data/world_bank.py
+++ b/superset/data/world_bank.py
@@ -71,7 +71,7 @@ def load_world_bank_health_n_pop():
 
     metrics = [
         'sum__SP_POP_TOTL', 'sum__SH_DYN_AIDS', 'sum__SH_DYN_AIDS',
-        'sum__SP_RUR_TOTL_ZS', 'sum__SP_DYN_LE00_IN',
+        'sum__SP_RUR_TOTL_ZS', 'sum__SP_DYN_LE00_IN', 'sum__SP_RUR_TOTL'
     ]
     for m in metrics:
         if not any(col.metric_name == m for col in tbl.metrics):
diff --git a/superset/db_engine_specs.py b/superset/db_engine_specs.py
index 5fed480..35a591f 100644
--- a/superset/db_engine_specs.py
+++ b/superset/db_engine_specs.py
@@ -37,6 +37,7 @@ import re
 import textwrap
 import time
 from typing import List, Tuple
+from urllib import parse
 
 from flask import g
 from flask_babel import lazy_gettext as _
@@ -577,6 +578,7 @@ class SnowflakeEngineSpec(PostgresBaseEngineSpec):
         if '/' in uri.database:
             database = uri.database.split('/')[0]
         if selected_schema:
+            selected_schema = parse.quote(selected_schema, safe='')
             uri.database = database + '/' + selected_schema
         return uri
 
@@ -757,7 +759,7 @@ class MySQLEngineSpec(BaseEngineSpec):
     @classmethod
     def adjust_database_uri(cls, uri, selected_schema=None):
         if selected_schema:
-            uri.database = selected_schema
+            uri.database = parse.quote(selected_schema, safe='')
         return uri
 
     @classmethod
@@ -1081,6 +1083,7 @@ class PrestoEngineSpec(BaseEngineSpec):
     def adjust_database_uri(cls, uri, selected_schema=None):
         database = uri.database
         if selected_schema and database:
+            selected_schema = parse.quote(selected_schema, safe='')
             if '/' in database:
                 database = database.split('/')[0] + '/' + selected_schema
             else:
@@ -1484,7 +1487,7 @@ class HiveEngineSpec(PrestoEngineSpec):
     @classmethod
     def adjust_database_uri(cls, uri, selected_schema=None):
         if selected_schema:
-            uri.database = selected_schema
+            uri.database = parse.quote(selected_schema, safe='')
         return uri
 
     @classmethod
diff --git a/superset/models/annotations.py b/superset/models/annotations.py
index 027f14f..1de9dfd 100644
--- a/superset/models/annotations.py
+++ b/superset/models/annotations.py
@@ -46,7 +46,7 @@ class Annotation(Model, AuditMixinNullable):
     id = Column(Integer, primary_key=True)
     start_dttm = Column(DateTime)
     end_dttm = Column(DateTime)
-    layer_id = Column(Integer, ForeignKey('annotation_layer.id'))
+    layer_id = Column(Integer, ForeignKey('annotation_layer.id'), nullable=False)
     short_descr = Column(String(500))
     long_descr = Column(Text)
     layer = relationship(
diff --git a/superset/sql_parse.py b/superset/sql_parse.py
index 2f65392..662f6c3 100644
--- a/superset/sql_parse.py
+++ b/superset/sql_parse.py
@@ -18,7 +18,7 @@
 import logging
 
 import sqlparse
-from sqlparse.sql import Identifier, IdentifierList
+from sqlparse.sql import Identifier, IdentifierList, Token, TokenList
 from sqlparse.tokens import Keyword, Name
 
 RESULT_OPERATIONS = {'UNION', 'INTERSECT', 'EXCEPT', 'SELECT'}
@@ -75,32 +75,32 @@ class ParsedQuery(object):
         return statements
 
     @staticmethod
-    def __get_full_name(identifier):
-        if len(identifier.tokens) > 2 and identifier.tokens[1].value == '.':
-            return '{}.{}'.format(identifier.tokens[0].value,
-                                  identifier.tokens[2].value)
-        return identifier.get_real_name()
+    def __get_full_name(tlist: TokenList):
+        if len(tlist.tokens) > 2 and tlist.tokens[1].value == '.':
+            return '{}.{}'.format(tlist.tokens[0].value,
+                                  tlist.tokens[2].value)
+        return tlist.get_real_name()
 
     @staticmethod
-    def __is_identifier(token):
+    def __is_identifier(token: Token):
         return isinstance(token, (IdentifierList, Identifier))
 
-    def __process_identifier(self, identifier):
+    def __process_tokenlist(self, tlist: TokenList):
         # exclude subselects
-        if '(' not in str(identifier):
-            table_name = self.__get_full_name(identifier)
+        if '(' not in str(tlist):
+            table_name = self.__get_full_name(tlist)
             if table_name and not table_name.startswith(CTE_PREFIX):
                 self._table_names.add(table_name)
             return
 
         # store aliases
-        if hasattr(identifier, 'get_alias'):
-            self._alias_names.add(identifier.get_alias())
-        if hasattr(identifier, 'tokens'):
-            # some aliases are not parsed properly
-            if identifier.tokens[0].ttype == Name:
-                self._alias_names.add(identifier.tokens[0].value)
-        self.__extract_from_token(identifier)
+        if tlist.has_alias():
+            self._alias_names.add(tlist.get_alias())
+
+        # some aliases are not parsed properly
+        if tlist.tokens[0].ttype == Name:
+            self._alias_names.add(tlist.tokens[0].value)
+        self.__extract_from_token(tlist)
 
     def as_create_table(self, table_name, overwrite=False):
         """Reformats the query into the create table as query.
@@ -144,10 +144,11 @@ class ParsedQuery(object):
 
             if table_name_preceding_token:
                 if isinstance(item, Identifier):
-                    self.__process_identifier(item)
+                    self.__process_tokenlist(item)
                 elif isinstance(item, IdentifierList):
                     for token in item.get_identifiers():
-                        self.__process_identifier(token)
+                        if isinstance(token, TokenList):
+                            self.__process_tokenlist(token)
             elif isinstance(item, IdentifierList):
                 for token in item.tokens:
                     if not self.__is_identifier(token):
diff --git a/superset/sql_validators/presto_db.py b/superset/sql_validators/presto_db.py
index 87c2d8e..8f2be43 100644
--- a/superset/sql_validators/presto_db.py
+++ b/superset/sql_validators/presto_db.py
@@ -26,7 +26,6 @@ from typing import (
 )
 
 from flask import g
-from pyhive.exc import DatabaseError
 
 from superset import app, security_manager
 from superset.sql_parse import ParsedQuery
@@ -77,6 +76,7 @@ class PrestoDBSQLValidator(BaseSQLValidator):
         # engine spec's handle_cursor implementation since we don't record
         # these EXPLAIN queries done in validation as proper Query objects
         # in the superset ORM.
+        from pyhive.exc import DatabaseError
         try:
             db_engine_spec.execute(cursor, sql)
             polled = cursor.poll()
diff --git a/superset/templates/superset/export_dashboards.html b/superset/templates/superset/export_dashboards.html
index 204ac2c..5ec6ff1 100644
--- a/superset/templates/superset/export_dashboards.html
+++ b/superset/templates/superset/export_dashboards.html
@@ -18,7 +18,16 @@
 #}
 <script>
     window.onload = function() {
-        window.open(window.location += '&action=go', '_blank');
-        window.location = '{{ dashboards_url }}';
+
+        // See issue #7353, window.open fails
+        var a = document.createElement('a');
+        a.href = window.location + '&action=go';
+        a.download = 'dashboards.json';
+        document.body.appendChild(a);
+        a.click();
+        document.body.removeChild(a);
+
+        window.location = '{{ dashboards_url }}';    
+
     };
 </script>
diff --git a/superset/translations/de/LC_MESSAGES/messages.json b/superset/translations/de/LC_MESSAGES/messages.json
index ca4eadb..0a71b27 100644
--- a/superset/translations/de/LC_MESSAGES/messages.json
+++ b/superset/translations/de/LC_MESSAGES/messages.json
@@ -1,2415 +1,4 @@
-{
-   "domain": "superset",
-   "locale_data": {
-      "superset": {
-         "": {
-            "domain": "superset",
-            "plural_forms": "nplurals=2; plural=(n != 1)",
-            "lang": "de"
-         },
-         "Time Column": [
-            "Zeitspalte"
-         ],
-         "second": [
-            "Sekunde"
-         ],
-         "minute": [
-            "Minute"
-         ],
-         "hour": [
-            "Stunde"
-         ],
-         "day": [
-            "Tag"
-         ],
-         "week": [
-            "Woche"
-         ],
-         "month": [
-            "Monat"
-         ],
-         "quarter": [
-            "Vierteljahr"
-         ],
-         "year": [
-            "Jahr"
-         ],
-         "week_start_monday": [
-            ""
-         ],
-         "week_ending_saturday": [
-            ""
-         ],
-         "week_start_sunday": [
-            ""
-         ],
-         "5 minute": [
-            "5 Minuten"
-         ],
-         "half hour": [
-            "halbe Stunde"
-         ],
-         "10 minute": [
-            "10 Minuten"
-         ],
-         "[Superset] Access to the datasource %(name)s was granted": [
-            "[Superset] Zugriff auf Datenquelle %(name)s war genehmigt "
-         ],
-         "Viz fehlt eine Datenquelle": [
-            ""
-         ],
-         "From date cannot be larger than to date": [
-            "'Von Datum' kann nicht größer als 'bis Datum' sein"
-         ],
-         "Table View": [
-            "Tabellenansicht"
-         ],
-         "Pick a granularity in the Time section or uncheck 'Include Time'": [
-            ""
-         ],
-         "Choose either fields to [Group By] and [Metrics] or [Columns], not both": [
-            ""
-         ],
-         "Pivot Table": [
-            ""
-         ],
-         "Please choose at least one \"Group by\" field ": [
-            ""
-         ],
-         "Please choose at least one metric": [
-            ""
-         ],
-         "'Group By' and 'Columns' can't overlap": [
-            ""
-         ],
-         "Markup": [
-            ""
-         ],
-         "Separator": [
-            ""
-         ],
-         "Word Cloud": [
-            ""
-         ],
-         "Treemap": [
-            ""
-         ],
-         "Calendar Heatmap": [
-            ""
-         ],
-         "Box Plot": [
-            ""
-         ],
-         "Bubble Chart": [
-            ""
-         ],
-         "Pick a metric for x, y and size": [
-            ""
-         ],
-         "Bullet Chart": [
-            ""
-         ],
-         "Pick a metric to display": [
-            ""
-         ],
-         "Big Number with Trendline": [
-            ""
-         ],
-         "Pick a metric!": [
-            ""
-         ],
-         "Big Number": [
-            ""
-         ],
-         "Time Series - Line Chart": [
-            ""
-         ],
-         "Pick a time granularity for your time series": [
-            ""
-         ],
-         "Time Series - Dual Axis Line Chart": [
-            ""
-         ],
-         "Pick a metric for left axis!": [
-            ""
-         ],
-         "Pick a metric for right axis!": [
-            ""
-         ],
-         "Please choose different metrics on left and right axis": [
-            ""
-         ],
-         "Time Series - Bar Chart": [
-            ""
-         ],
-         "Time Series - Percent Change": [
-            ""
-         ],
-         "Time Series - Stacked": [
-            ""
-         ],
-         "Distribution - NVD3 - Pie Chart": [
-            ""
-         ],
-         "Histogram": [
-            "Histogramm"
-         ],
-         "Must have one numeric column specified": [
-            ""
-         ],
-         "Distribution - Bar Chart": [
-            ""
-         ],
-         "Can't have overlap between Series and Breakdowns": [
-            ""
-         ],
-         "Pick at least one metric": [
-            ""
-         ],
-         "Pick at least one field for [Series]": [
-            ""
-         ],
-         "Sunburst": [
-            ""
-         ],
-         "Sankey": [
-            ""
-         ],
-         "Pick exactly 2 columns as [Source / Target]": [
-            ""
-         ],
-         "There's a loop in your Sankey, please provide a tree. Here's a faulty link: {}": [
-            ""
-         ],
-         "Directed Force Layout": [
-            ""
-         ],
-         "Pick exactly 2 columns to 'Group By'": [
-            ""
-         ],
-         "Country Map": [
-            ""
-         ],
-         "World Map": [
-            ""
-         ],
-         "Filters": [
-            ""
-         ],
-         "Pick at least one filter field": [
-            ""
-         ],
-         "iFrame": [
-            ""
-         ],
-         "Parallel Coordinates": [
-            ""
-         ],
-         "Heatmap": [
-            ""
-         ],
-         "Horizon Charts": [
-            ""
-         ],
-         "Mapbox": [
-            ""
-         ],
-         "Must have a [Group By] column to have 'count' as the [Label]": [
-            ""
-         ],
-         "Choice of [Label] must be present in [Group By]": [
-            ""
-         ],
-         "Choice of [Point Radius] must be present in [Group By]": [
-            ""
-         ],
-         "[Longitude] and [Latitude] columns must be present in [Group By]": [
-            ""
-         ],
-         "Event flow": [
-            ""
-         ],
-         "Time Series - Paired t-test": [
-            ""
-         ],
-         "Your query was saved": [
-            "Ihre Abfrage wurde gespeichert"
-         ],
-         "Your query could not be saved": [
-            "Ihre Abfrage konnte nicht gespeichert werden"
-         ],
-         "Failed at retrieving results from the results backend": [
-            ""
-         ],
-         "Could not connect to server": [
-            ""
-         ],
-         "Your session timed out, please refresh your page and try again.": [
-            ""
-         ],
-         "Query was stopped.": [
-            ""
-         ],
-         "Failed at stopping query.": [
-            ""
-         ],
-         "Error occurred while fetching table metadata": [
-            ""
-         ],
-         "shared query": [
-            ""
-         ],
-         "The query couldn't be loaded": [
-            ""
-         ],
-         "An error occurred while creating the data source": [
-            ""
-         ],
-         "Pick a chart type!": [
-            ""
-         ],
-         "To use this chart type you need at least one column flagged as a date": [
-            ""
-         ],
-         "To use this chart type you need at least one dimension": [
-            ""
-         ],
-         "To use this chart type you need at least one aggregation function": [
-            ""
-         ],
-         "Untitled Query": [
-            ""
-         ],
-         "Copy of %s": [
-            ""
-         ],
-         "share query": [
-            ""
-         ],
-         "copy URL to clipboard": [
-            ""
-         ],
-         "Raw SQL": [
-            ""
-         ],
-         "Source SQL": [
-            ""
-         ],
-         "SQL": [
-            ""
-         ],
-         "No query history yet...": [
-            ""
-         ],
-         "It seems you don't have access to any database": [
-            ""
-         ],
-         "Search Results": [
-            ""
-         ],
-         "[From]-": [
-            ""
-         ],
-         "[To]-": [
-            ""
-         ],
-         "[Query Status]": [
-            ""
-         ],
-         "Search": [
-            "Suche"
-         ],
-         "Open in SQL Editor": [
-            "Bearbeiten in SQL Editor"
-         ],
-         "view results": [
-            ""
-         ],
-         "Data preview": [
-            ""
-         ],
-         "Visualize the data out of this query": [
-            ""
-         ],
-         "Overwrite text in editor with a query on this table": [
-            ""
-         ],
-         "Run query in a new tab": [
-            ""
-         ],
-         "Remove query from log": [
-            ""
-         ],
-         ".CSV": [
-            ""
-         ],
-         "Visualize": [
-            "Visualalisieren"
-         ],
-         "Table": [
-            "Tabellen"
-         ],
-         "was created": [
-            ""
-         ],
-         "Query in a new tab": [
-            ""
-         ],
-         "Fetch data preview": [
-            ""
-         ],
-         "Track Job": [
-            ""
-         ],
-         "Loading...": [
-            ""
-         ],
-         "Run Selected Query": [
-            ""
-         ],
-         "Run Query": [
-            ""
-         ],
-         "Run query asynchronously": [
-            ""
-         ],
-         "Stop": [
-            "Stopp"
-         ],
-         "Undefined": [
-            ""
-         ],
-         "Label": [
-            ""
-         ],
-         "Label for your query": [
-            ""
-         ],
-         "Description": [
-            ""
-         ],
-         "Write a description for your query": [
-            ""
-         ],
-         "Save": [
-            ""
-         ],
-         "Cancel": [
-            ""
-         ],
-         "Save Query": [
-            ""
-         ],
-         "Run a query to display results here": [
-            ""
-         ],
-         "Preview for %s": [
-            ""
-         ],
-         "Results": [
-            ""
-         ],
-         "Query History": [
-            ""
-         ],
-         "Create table as with query results": [
-            ""
-         ],
-         "new table name": [
-            ""
-         ],
-         "Error while fetching table list": [
-            ""
-         ],
-         "Error while fetching schema list": [
-            ""
-         ],
-         "Error while fetching database list": [
-            ""
-         ],
-         "Database:": [
-            ""
-         ],
-         "Select a database": [
-            ""
-         ],
-         "Select a schema (%s)": [
-            ""
-         ],
-         "Schema:": [
-            ""
-         ],
-         "Add a table (%s)": [
-            ""
-         ],
-         "Type to search ...": [
-            ""
-         ],
-         "Reset State": [
-            ""
-         ],
-         "Enter a new title for the tab": [
-            ""
-         ],
-         "Untitled Query %s": [
-            ""
-         ],
-         "close tab": [
-            ""
-         ],
-         "rename tab": [
-            ""
-         ],
-         "expand tool bar": [
-            ""
-         ],
-         "hide tool bar": [
-            ""
-         ],
-         "Copy partition query to clipboard": [
-            ""
-         ],
-         "latest partition:": [
-            ""
-         ],
-         "Keys for table": [
-            ""
-         ],
-         "View keys & indexes (%s)": [
-            ""
-         ],
-         "Sort columns alphabetically": [
-            ""
-         ],
-         "Original table column order": [
-            ""
-         ],
-         "Copy SELECT statement to clipboard": [
-            ""
-         ],
-         "Remove table preview": [
-            ""
-         ],
-         "%s is not right as a column name, please alias it (as in SELECT count(*) ": [
-            ""
-         ],
-         "AS my_alias": [
-            ""
-         ],
-         "using only alphanumeric characters and underscores": [
-            ""
-         ],
-         "Creating a data source and popping a new tab": [
-            ""
-         ],
-         "No results available for this query": [
-            ""
-         ],
-         "Chart Type": [
-            ""
-         ],
-         "[Chart Type]": [
-            ""
-         ],
-         "Datasource Name": [
-            ""
-         ],
-         "datasource name": [
-            ""
-         ],
-         "Select ...": [
-            ""
-         ],
-         "Loaded data cached": [
-            ""
-         ],
-         "Loaded from cache": [
-            ""
-         ],
-         "Click to force-refresh": [
-            ""
-         ],
-         "Copy to clipboard": [
-            ""
-         ],
-         "Not successful": [
-            ""
-         ],
-         "Sorry, your browser does not support copying. Use Ctrl / Cmd + C!": [
-            ""
-         ],
-         "Copied!": [
-            ""
-         ],
-         "Title": [
-            "Titel"
-         ],
-         "click to edit title": [
-            ""
-         ],
-         "You don't have the rights to alter this title.": [
-            ""
-         ],
-         "Click to favorite/unfavorite": [
-            ""
-         ],
-         "You have unsaved changes.": [
-            ""
-         ],
-         "Click the": [
-            ""
-         ],
-         "button on the top right to save your changes.": [
-            ""
-         ],
-         "Served from data cached %s . Click to force refresh.": [
-            "Von Daten dienten um %s im Cache gespeichert. Aktualisierung erzwingen durch Klicken"
-         ],
-         "Click to force refresh": [
-            "Aktualisierung erzwingen"
-         ],
-         "Error": [
-            "Fehler"
-         ],
-         "Sorry, there was an error adding slices to this dashboard: %s": [
-            ""
-         ],
-         "Active Dashboard Filters": [
-            ""
-         ],
-         "Checkout this dashboard: %s": [
-            ""
-         ],
-         "Force refresh the whole dashboard": [
-            "Ganze Dashboard Aktualisierung erzwingen"
-         ],
-         "Edit this dashboard's properties": [
-            "Dashboardeigenschaften bearbeiten"
-         ],
-         "Load a template": [
-            "Vorlage laden"
-         ],
-         "Load a CSS template": [
-            "CSS Vorlage laden"
-         ],
-         "CSS": [
-            "CSS"
-         ],
-         "Live CSS Editor": [
-            ""
-         ],
-         "Don't refresh": [
-            "Nicht aktualisieren"
-         ],
-         "10 seconds": [
-            "10 Sekunden"
-         ],
-         "30 seconds": [
-            "30 Sekunden"
-         ],
-         "1 minute": [
-            "1 Minute"
-         ],
-         "5 minutes": [
-            "5 Minuten"
-         ],
-         "Refresh Interval": [
-            "Aktualisierung Zwischenzeit"
-         ],
-         "Choose the refresh frequency for this dashboard": [
-            "Aktualisierungsfrequenz auswählen für dieses Dashboard"
-         ],
-         "This dashboard was saved successfully.": [
-            "Dashboard wurde erfolgreich gespeichert"
-         ],
-         "Sorry, there was an error saving this dashboard: ": [
-            ""
-         ],
-         "You must pick a name for the new dashboard": [
-            ""
-         ],
-         "Save Dashboard": [
-            "Dashboard speichern"
-         ],
-         "Overwrite Dashboard [%s]": [
-            ""
-         ],
-         "Save as:": [
-            "Speichern als:"
-         ],
-         "[dashboard name]": [
-            ""
-         ],
-         "Name": [
-            "Name"
-         ],
-         "Viz": [
-            ""
-         ],
-         "Modified": [
-            "Geändert"
-         ],
-         "Add Slices": [
-            ""
-         ],
-         "Add a new slice to the dashboard": [
-            ""
-         ],
-         "Add Slices to Dashboard": [
-            ""
-         ],
-         "Move chart": [
-            "Diagramm bewegen"
-         ],
-         "Force refresh data": [
-            "Aktualisierung erzwingen"
-         ],
-         "Toggle chart description": [
-            "Diagramm Beschreibung umschalten"
-         ],
-         "Edit chart": [
-            "Diagramm bearbeiten"
-         ],
-         "Export CSV": [
-            "Exportieren als CSV"
-         ],
-         "Explore chart": [
-            "Diagramm untersuchen"
-         ],
-         "Remove chart from dashboard": [
-            "Diagramm von Dashboard entfernen"
-         ],
-         "%s - untitled": [
-            "%s - unbenannt"
-         ],
-         "Edit slice properties": [
-            ""
-         ],
-         "description": [
-            "Beschreibung"
-         ],
-         "bolt": [
-            ""
-         ],
-         "Error...": [
-            "Fehler..."
-         ],
-         "Query": [
-            "Abfrage"
-         ],
-         "Height": [
-            "Höhe"
-         ],
-         "Width": [
-            "Breite"
-         ],
-         "Export to .json": [
-            "Exportieren als JSON"
-         ],
-         "Export to .csv format": [
-            "Exportieren als CSV"
-         ],
-         "Please enter a slice name": [
-            "Bitten Scheibename eingeben"
-         ],
-         "Please select a dashboard": [
-            "Bitte Dashboard auswählen"
-         ],
-         "Please enter a dashboard name": [
-            "Bitte Dashboardname eingeben"
-         ],
-         "Save A Slice": [
-            "Scheibe speichern"
-         ],
-         "Overwrite slice %s": [
-            "Überschreiben Scheibe %s"
-         ],
-         "Save as": [
-            "Speichern als"
-         ],
-         "[slice name]": [
-            ""
-         ],
-         "Do not add to a dashboard": [
-            "Nicht zum Dashboard hinzufügen"
-         ],
-         "Add slice to existing dashboard": [
-            "Schiebe zum vorhandenen Dashboard hinzufügen"
-         ],
-         "Add to new dashboard": [
-            "Schiebe zum neuen Dashboard hinzufügen"
-         ],
-         "Save & go to dashboard": [
-            "Speichern & zum Dashboard gehen"
-         ],
-         "Check out this slice: %s": [
-            ""
-         ],
-         "`Min` value should be numeric or empty": [
-            ""
-         ],
-         "`Max` value should be numeric or empty": [
-            ""
-         ],
-         "Min": [
-            ""
-         ],
-         "Max": [
-            ""
-         ],
-         "Something went wrong while fetching the datasource list": [
-            ""
-         ],
-         "Click to point to another datasource": [
-            ""
-         ],
-         "Edit the datasource's configuration": [
-            ""
-         ],
-         "Select a datasource": [
-            ""
-         ],
-         "Search / Filter": [
-            ""
-         ],
-         "Filter value": [
-            ""
-         ],
-         "Select metric": [
-            ""
-         ],
-         "Select column": [
-            ""
-         ],
-         "Select operator": [
-            ""
-         ],
-         "Add Filter": [
-            ""
-         ],
-         "Error while fetching data": [
-            ""
-         ],
-         "Select %s": [
-            ""
-         ],
-         "textarea": [
-            ""
-         ],
-         "Edit": [
-            ""
-         ],
-         "in modal": [
-            ""
-         ],
-         "Select a visualization type": [
-            "Visualisierungstyp wählen"
-         ],
-         "Updating chart was stopped": [
-            ""
-         ],
-         "An error occurred while rendering the visualization: %s": [
-            ""
-         ],
-         "Perhaps your data has grown, your database is under unusual load, or you are simply querying a data source that is to large to be processed within the timeout range. If that is the case, we recommend that you summarize your data further.": [
-            ""
-         ],
-         "Network error.": [
-            ""
-         ],
-         "A reference to the [Time] configuration, taking granularity into account": [
-            ""
-         ],
-         "Group by": [
-            ""
-         ],
-         "One or many controls to group by": [
-            ""
-         ],
-         "Datasource": [
-            "Datenquelle"
-         ],
-         "Visualization Type": [
-            "Visualisierungstyp"
-         ],
-         "The type of visualization to display": [
-            "Der Visualisierungstyp anzuzeigen"
-         ],
-         "Metrics": [
-            "Metriken"
-         ],
-         "One or many metrics to display": [
-            ""
-         ],
-         "Y Axis Bounds": [
-            ""
-         ],
-         "Bounds for the Y axis. When left empty, the bounds are dynamically defined based on the min/max of the data. Note that this feature will only expand the axis range. It won't narrow the data's extent.": [
-            ""
-         ],
-         "Ordering": [
-            ""
-         ],
-         "Annotation Layers": [
-            "Anmerkungstufe"
-         ],
-         "Annotation layers to overlay on the visualization": [
-            "Anmerkungstufe auf die Visualisierung zu legen"
-         ],
-         "Select a annotation layer": [
-            "Anmerkungstufe auswählen"
-         ],
-         "Error while fetching annotation layers": [
-            "Fehler bei Anmerkungstufeabruf"
-         ],
-         "Metric": [
-            "Metrik"
-         ],
-         "Choose the metric": [
-            "Metrik auswählen"
-         ],
-         "Right Axis Metric": [
-            ""
-         ],
-         "Choose a metric for right axis": [
-            ""
-         ],
-         "Stacked Style": [
-            ""
-         ],
-         "Linear Color Scheme": [
-            ""
-         ],
-         "Normalize Across": [
-            ""
-         ],
-         "Color will be rendered based on a ratio of the cell against the sum of across this criteria": [
-            ""
-         ],
-         "Horizon Color Scale": [
-            ""
-         ],
-         "Defines how the color are attributed.": [
-            ""
-         ],
-         "Rendering": [
-            ""
-         ],
-         "image-rendering CSS attribute of the canvas object that defines how the browser scales up the image": [
-            ""
-         ],
-         "XScale Interval": [
-            ""
-         ],
-         "Number of steps to take between ticks when displaying the X scale": [
-            ""
-         ],
-         "YScale Interval": [
-            ""
-         ],
-         "Number of steps to take between ticks when displaying the Y scale": [
-            ""
-         ],
-         "Include Time": [
-            ""
-         ],
-         "Whether to include the time granularity as defined in the time section": [
-            ""
-         ],
-         "Stacked Bars": [
-            ""
-         ],
-         "Show totals": [
-            ""
-         ],
-         "Display total row/column": [
-            ""
-         ],
-         "Show Markers": [
-            ""
-         ],
-         "Show data points as circle markers on the lines": [
-            ""
-         ],
-         "Bar Values": [
-            ""
-         ],
-         "Show the value on top of the bar": [
-            ""
-         ],
-         "Sort Bars": [
-            ""
-         ],
-         "Sort bars by x labels.": [
-            ""
-         ],
-         "Combine Metrics": [
-            ""
-         ],
-         "Display metrics side by side within each column, as opposed to each column being displayed side by side for each metric.": [
-            ""
-         ],
-         "Extra Controls": [
-            ""
-         ],
-         "Whether to show extra controls or not. Extra controls include things like making mulitBar charts stacked or side by side.": [
-            ""
-         ],
-         "Reduce X ticks": [
-            ""
-         ],
-         "Reduces the number of X axis ticks to be rendered. If true, the x axis wont overflow and labels may be missing. If false, a minimum width will be applied to columns and the width may overflow into an horizontal scroll.": [
-            ""
-         ],
-         "Include Series": [
-            ""
-         ],
-         "Include series name as an axis": [
-            ""
-         ],
-         "Color Metric": [
-            ""
-         ],
-         "A metric to use for color": [
-            ""
-         ],
-         "Country Name": [
-            ""
-         ],
-         "The name of country that Superset should display": [
-            ""
-         ],
-         "Country Field Type": [
-            ""
-         ],
-         "The country code standard that Superset should expect to find in the [country] column": [
-            ""
-         ],
-         "Columns": [
-            ""
-         ],
-         "One or many controls to pivot as columns": [
-            ""
-         ],
-         "Columns to display": [
-            ""
-         ],
-         "Origin": [
-            ""
-         ],
-         "Defines the origin where time buckets start, accepts natural dates as in `now`, `sunday` or `1970-01-01`": [
-            ""
-         ],
-         "Bottom Margin": [
-            ""
-         ],
-         "Bottom margin, in pixels, allowing for more room for axis labels": [
-            ""
-         ],
-         "Left Margin": [
-            ""
-         ],
-         "Left margin, in pixels, allowing for more room for axis labels": [
-            ""
-         ],
-         "Time Granularity": [
-            ""
-         ],
-         "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`": [
-            ""
-         ],
-         "Domain": [
-            ""
-         ],
-         "The time unit used for the grouping of blocks": [
-            ""
-         ],
-         "Subdomain": [
-            ""
-         ],
-         "The time unit for each block. Should be a smaller unit than domain_granularity. Should be larger or equal to Time Grain": [
-            ""
-         ],
-         "Link Length": [
-            ""
-         ],
-         "Link length in the force layout": [
-            ""
-         ],
-         "Charge": [
-            ""
-         ],
-         "Charge in the force layout": [
-            ""
-         ],
-         "The time column for the visualization. Note that you can define arbitrary expression that return a DATETIME column in the table or. Also note that the filter below is applied against this column or expression": [
-            ""
-         ],
-         "Time Grain": [
-            ""
-         ],
-         "The time granularity for the visualization. This applies a date transformation to alter your time column and defines a new time granularity. The options here are defined on a per database engine basis in the Superset source code.": [
-            ""
-         ],
-         "Resample Rule": [
-            ""
-         ],
-         "Pandas resample rule": [
-            ""
-         ],
-         "Resample How": [
-            ""
-         ],
-         "Pandas resample how": [
-            ""
-         ],
-         "Resample Fill Method": [
-            ""
-         ],
-         "Pandas resample fill method": [
-            ""
-         ],
-         "Since": [
-            ""
-         ],
-         "7 days ago": [
-            ""
-         ],
-         "Until": [
-            ""
-         ],
-         "Max Bubble Size": [
-            ""
-         ],
-         "Whisker/outlier options": [
-            ""
-         ],
-         "Determines how whiskers and outliers are calculated.": [
-            ""
-         ],
-         "Ratio": [
-            ""
-         ],
-         "Target aspect ratio for treemap tiles.": [
-            ""
-         ],
-         "Number format": [
-            ""
-         ],
-         "Row limit": [
-            ""
-         ],
-         "Series limit": [
-            ""
-         ],
-         "Limits the number of time series that get displayed": [
-            ""
-         ],
-         "Sort By": [
-            ""
-         ],
-         "Metric used to define the top series": [
-            ""
-         ],
-         "Rolling": [
-            ""
-         ],
-         "Defines a rolling window function to apply, works along with the [Periods] text box": [
-            ""
-         ],
-         "Periods": [
-            ""
-         ],
-         "Defines the size of the rolling window function, relative to the time granularity selected": [
-            ""
-         ],
-         "Min Periods": [
-            ""
-         ],
-         "The minimum number of rolling periods required to show a value. For instance if you do a cumulative sum on 7 days you may want your \"Min Period\" to be 7, so that all data points shown are the total of 7 periods. This will hide the \"ramp up\" taking place over the first 7 periods": [
-            ""
-         ],
-         "Series": [
-            ""
-         ],
-         "Defines the grouping of entities. Each series is shown as a specific color on the chart and has a legend toggle": [
-            ""
-         ],
-         "Entity": [
-            ""
-         ],
-         "This defines the element to be plotted on the chart": [
-            ""
-         ],
-         "X Axis": [
-            ""
-         ],
-         "Metric assigned to the [X] axis": [
-            ""
-         ],
-         "Y Axis": [
-            ""
-         ],
-         "Metric assigned to the [Y] axis": [
-            ""
-         ],
-         "Bubble Size": [
-            ""
-         ],
-         "URL": [
-            ""
-         ],
-         "The URL, this control is templated, so you can integrate {{ width }} and/or {{ height }} in your URL string.": [
-            ""
-         ],
-         "X Axis Label": [
-            ""
-         ],
-         "Y Axis Label": [
-            ""
-         ],
-         "Custom WHERE clause": [
-            ""
-         ],
-         "The text in this box gets included in your query's WHERE clause, as an AND to other criteria. You can include complex expression, parenthesis and anything else supported by the backend it is directed towards.": [
-            ""
-         ],
-         "Custom HAVING clause": [
-            ""
-         ],
-         "The text in this box gets included in your query's HAVING clause, as an AND to other criteria. You can include complex expression, parenthesis and anything else supported by the backend it is directed towards.": [
-            ""
-         ],
-         "Comparison Period Lag": [
-            ""
-         ],
-         "Based on granularity, number of time periods to compare against": [
-            ""
-         ],
-         "Comparison suffix": [
-            ""
-         ],
-         "Suffix to apply after the percentage display": [
-            ""
-         ],
-         "Table Timestamp Format": [
-            ""
-         ],
-         "Timestamp Format": [
-            ""
-         ],
-         "Series Height": [
-            ""
-         ],
-         "Pixel height of each series": [
-            ""
-         ],
-         "Page Length": [
-            ""
-         ],
-         "Rows per page, 0 means no pagination": [
-            ""
-         ],
-         "X Axis Format": [
-            ""
-         ],
-         "Y Axis Format": [
-            ""
-         ],
-         "Right Axis Format": [
-            ""
-         ],
-         "Markup Type": [
-            ""
-         ],
-         "Pick your favorite markup language": [
-            ""
-         ],
-         "Rotation": [
-            ""
-         ],
-         "Rotation to apply to words in the cloud": [
-            ""
-         ],
-         "Line Style": [
-            ""
-         ],
-         "Line interpolation as defined by d3.js": [
-            ""
-         ],
-         "Label Type": [
-            ""
-         ],
-         "What should be shown on the label?": [
-            ""
-         ],
-         "Code": [
-            ""
-         ],
-         "Put your code here": [
-            ""
-         ],
-         "Aggregation function": [
-            ""
-         ],
-         "Aggregate function to apply when pivoting and computing the total rows and columns": [
-            ""
-         ],
-         "Font Size From": [
-            ""
-         ],
-         "Font size for the smallest value in the list": [
-            ""
-         ],
-         "Font Size To": [
-            ""
-         ],
-         "Font size for the biggest value in the list": [
-            ""
-         ],
-         "Instant Filtering": [
-            ""
-         ],
-         "Range Filter": [
-            ""
-         ],
-         "Whether to display the time range interactive selector": [
-            ""
-         ],
-         "Date Filter": [
-            ""
-         ],
-         "Whether to include a time filter": [
-            ""
-         ],
-         "Data Table": [
-            ""
-         ],
-         "Whether to display the interactive data table": [
-            ""
-         ],
-         "Search Box": [
-            ""
-         ],
-         "Whether to include a client side search box": [
-            ""
-         ],
-         "Table Filter": [
-            ""
-         ],
-         "Whether to apply filter when table cell is clicked": [
-            ""
-         ],
-         "Show Bubbles": [
-            ""
-         ],
-         "Whether to display bubbles on top of countries": [
-            ""
-         ],
-         "Legend": [
-            ""
-         ],
-         "Whether to display the legend (toggles)": [
-            ""
-         ],
-         "X bounds": [
-            ""
-         ],
-         "Whether to display the min and max values of the X axis": [
-            ""
-         ],
-         "Y bounds": [
-            ""
-         ],
-         "Whether to display the min and max values of the Y axis": [
-            ""
-         ],
-         "Rich Tooltip": [
-            ""
-         ],
-         "The rich tooltip shows a list of all series for that point in time": [
-            ""
-         ],
-         "Y Log Scale": [
-            ""
-         ],
-         "Use a log scale for the Y axis": [
-            ""
-         ],
-         "X Log Scale": [
-            ""
-         ],
-         "Use a log scale for the X axis": [
-            ""
-         ],
-         "Donut": [
-            ""
-         ],
-         "Do you want a donut or a pie?": [
-            ""
-         ],
-         "Put labels outside": [
-            ""
-         ],
-         "Put the labels outside the pie?": [
-            ""
-         ],
-         "Contribution": [
-            ""
-         ],
-         "Compute the contribution to the total": [
-            ""
-         ],
-         "Period Ratio": [
-            ""
-         ],
-         "[integer] Number of period to compare against, this is relative to the granularity selected": [
-            ""
-         ],
-         "Period Ratio Type": [
-            ""
-         ],
-         "`factor` means (new/previous), `growth` is ((new/previous) - 1), `value` is (new-previous)": [
-            ""
-         ],
-         "Time Shift": [
-            ""
-         ],
-         "Overlay a timeseries from a relative time period. Expects relative time delta in natural language (example:  24 hours, 7 days, 56 weeks, 365 days)": [
-            ""
-         ],
-         "Subheader": [
-            ""
-         ],
-         "Description text that shows up below your Big Number": [
-            ""
-         ],
-         "label": [
-            ""
-         ],
-         "`count` is COUNT(*) if a group by is used. Numerical columns will be aggregated with the aggregator. Non-numerical columns will be used to label points. Leave empty to get a count of points in each cluster.": [
-            ""
-         ],
-         "Map Style": [
-            ""
-         ],
-         "Base layer map style": [
-            ""
-         ],
-         "Clustering Radius": [
-            ""
-         ],
-         "The radius (in pixels) the algorithm uses to define a cluster. Choose 0 to turn off clustering, but beware that a large number of points (>1000) will cause lag.": [
-            ""
-         ],
-         "Point Radius": [
-            ""
-         ],
-         "The radius of individual points (ones that are not in a cluster). Either a numerical column or `Auto`, which scales the point based on the largest cluster": [
-            ""
-         ],
-         "Point Radius Unit": [
-            ""
-         ],
-         "The unit of measure for the specified point radius": [
-            ""
-         ],
-         "Opacity": [
-            ""
-         ],
-         "Opacity of all clusters, points, and labels. Between 0 and 1.": [
-            ""
-         ],
-         "Zoom": [
-            ""
-         ],
-         "Zoom level of the map": [
-            ""
-         ],
-         "Default latitude": [
-            ""
-         ],
-         "Latitude of default viewport": [
-            ""
-         ],
-         "Default longitude": [
-            ""
-         ],
-         "Longitude of default viewport": [
-            ""
-         ],
-         "Live render": [
-            ""
-         ],
-         "Points and clusters will update as viewport is being changed": [
-            ""
-         ],
-         "RGB Color": [
-            ""
-         ],
-         "The color for points and clusters in RGB": [
-            ""
-         ],
-         "Ranges": [
-            ""
-         ],
-         "Ranges to highlight with shading": [
-            ""
-         ],
-         "Range labels": [
-            ""
-         ],
-         "Labels for the ranges": [
-            ""
-         ],
-         "Markers": [
-            ""
-         ],
-         "List of values to mark with triangles": [
-            ""
-         ],
-         "Marker labels": [
-            ""
-         ],
-         "Labels for the markers": [
-            ""
-         ],
-         "Marker lines": [
-            ""
-         ],
-         "List of values to mark with lines": [
-            ""
-         ],
-         "Marker line labels": [
-            ""
-         ],
-         "Labels for the marker lines": [
-            ""
-         ],
-         "Slice ID": [
-            ""
-         ],
-         "The id of the active slice": [
-            ""
-         ],
-         "Cache Timeout (seconds)": [
-            ""
-         ],
-         "The number of seconds before expiring the cache": [
-            ""
-         ],
-         "Order by entity id": [
-            ""
-         ],
-         "Important! Select this if the table is not already sorted by entity id, else there is no guarantee that all events for each entity are returned.": [
-            ""
-         ],
-         "Minimum leaf node event count": [
-            ""
-         ],
-         "Leaf nodes that represent fewer than this number of events will be initially hidden in the visualization": [
-            ""
-         ],
-         "Color Scheme": [
-            "Farbschema"
-         ],
-         "The color scheme for rendering chart": [
-            ""
-         ],
-         "Time": [
-            ""
-         ],
-         "Time related form attributes": [
-            ""
-         ],
-         "Datasource & Chart Type": [
-            ""
-         ],
-         "This section exposes ways to include snippets of SQL in your query": [
-            ""
-         ],
-         "Annotations": [
-            "Anmerkungen"
-         ],
-         "Advanced Analytics": [
-            ""
-         ],
-         "This section contains options that allow for advanced analytical post processing of query results": [
-            ""
-         ],
-         "Result Filters": [
-            ""
-         ],
-         "The filters to apply after post-aggregation.Leave the value control empty to filter empty strings or nulls": [
-            ""
-         ],
-         "Chart Options": [
-            ""
-         ],
-         "Breakdowns": [
-            ""
-         ],
-         "Defines how each series is broken down": [
-            ""
-         ],
-         "Pie Chart": [
-            ""
-         ],
-         "Dual Axis Line Chart": [
-            ""
-         ],
-         "Y Axis 1": [
-            ""
-         ],
-         "Y Axis 2": [
-            ""
-         ],
-         "Left Axis Metric": [
-            ""
-         ],
-         "Choose a metric for left axis": [
-            ""
-         ],
-         "Left Axis Format": [
-            ""
-         ],
-         "Axes": [
-            ""
-         ],
-         "GROUP BY": [
-            ""
-         ],
-         "Use this section if you want a query that aggregates": [
-            ""
-         ],
-         "NOT GROUPED BY": [
-            ""
-         ],
-         "Use this section if you want to query atomic rows": [
-            ""
-         ],
-         "Options": [
-            ""
-         ],
-         "Bubbles": [
-            ""
-         ],
-         "Numeric Column": [
-            ""
-         ],
-         "Select the numeric column to draw the histogram": [
-            ""
-         ],
-         "No of Bins": [
-            ""
-         ],
-         "Select number of bins for the histogram": [
-            ""
-         ],
-         "Primary Metric": [
-            ""
-         ],
-         "The primary metric is used to define the arc segment sizes": [
-            ""
-         ],
-         "Secondary Metric": [
-            ""
-         ],
-         "This secondary metric is used to define the color as a ratio against the primary metric. If the two metrics match, color is mapped level groups": [
-            ""
-         ],
-         "Hierarchy": [
-            "Hierarchie"
-         ],
-         "This defines the level of the hierarchy": [
-            ""
-         ],
-         "Source / Target": [
-            ""
-         ],
-         "Choose a source and a target": [
-            ""
-         ],
-         "Chord Diagram": [
-            ""
-         ],
-         "Choose a number format": [
-            ""
-         ],
-         "Source": [
-            ""
-         ],
-         "Choose a source": [
-            ""
-         ],
-         "Target": [
-            ""
-         ],
-         "Choose a target": [
-            ""
-         ],
-         "ISO 3166-1 codes of region/province/department": [
-            ""
-         ],
-         "It's ISO 3166-1 of your region/province/department in your table. (see documentation for list of ISO 3166-1)": [
-            ""
-         ],
-         "Country Control": [
-            ""
-         ],
-         "3 letter code of the country": [
-            ""
-         ],
-         "Metric for color": [
-            ""
-         ],
-         "Metric that defines the color of the country": [
-            ""
-         ],
-         "Bubble size": [
-            ""
-         ],
-         "Metric that defines the size of the bubble": [
-            ""
-         ],
-         "Filter Box": [
-            ""
-         ],
-         "Filter controls": [
-            ""
-         ],
-         "The controls you want to filter on. Note that only columns checked as \"filterable\" will show up on this list.": [
-            ""
-         ],
-         "Heatmap Options": [
-            ""
-         ],
-         "Horizon": [
-            ""
-         ],
-         "Points": [
-            ""
-         ],
-         "Labelling": [
-            ""
-         ],
-         "Visual Tweaks": [
-            ""
-         ],
-         "Viewport": [
-            ""
-         ],
-         "Longitude": [
-            ""
-         ],
-         "Column containing longitude data": [
-            ""
-         ],
-         "Latitude": [
-            ""
-         ],
-         "Column containing latitude data": [
-            ""
-         ],
-         "Cluster label aggregator": [
-            ""
-         ],
-         "Aggregate function applied to the list of points in each cluster to produce the cluster label.": [
-            ""
-         ],
-         "Tooltip": [
-            ""
-         ],
-         "Show a tooltip when hovering over points and clusters describing the label": [
-            ""
-         ],
-         "One or many controls to group by. If grouping, latitude and longitude columns must be present.": [
-            ""
-         ],
-         "Event definition": [
-            ""
-         ],
-         "Additional meta data": [
-            ""
-         ],
-         "Column containing entity ids": [
-            ""
-         ],
-         "e.g., a \"user id\" column": [
-            ""
-         ],
-         "Column containing event names": [
-            ""
-         ],
-         "Event count limit": [
-            ""
-         ],
-         "The maximum number of events to return, equivalent to number of rows": [
-            ""
-         ],
-         "Meta data": [
-            ""
-         ],
-         "Select any columns for meta data inspection": [
-            ""
-         ],
-         "The server could not be reached. You may want to verify your connection and try again.": [
-            ""
-         ],
-         "An unknown error occurred. (Status: %s )": [
-            ""
-         ],
-         "Favorites": [
-            "Favoriten"
-         ],
-         "Created Content": [
-            "Erstellte Inhalt"
-         ],
-         "Recent Activity": [
-            "Kürzliche Aktivitäten"
-         ],
-         "Security & Access": [
-            "Sicherheit & Zugriff"
-         ],
-         "No slices": [
-            "Keine Schieben"
-         ],
-         "No dashboards": [
-            "Keine Dashboards"
-         ],
-         "Dashboards": [
-            "Dashboards"
-         ],
-         "Slices": [
-            "Schieben"
-         ],
-         "No favorite slices yet, go click on stars!": [
-            "Noch keine Lieblingsscheiben, klicken Sie auf ein paar Sterne!"
-         ],
-         "No favorite dashboards yet, go click on stars!": [
-            "Noch keine Lieblingsdashboards, klicken Sie auf ein paar Sterne!"
-         ],
-         "Roles": [
-            "Rollen"
-         ],
-         "Databases": [
-            "Datenbanken"
-         ],
-         "Datasources": [
-            "Datenquellen"
-         ],
-         "Profile picture provided by Gravatar": [
-            "Profilbild von Gravatar"
-         ],
-         "joined": [
-            ""
-         ],
-         "id:": [
-            ""
-         ],
-         "Sorry, there appears to be no data": [
-            ""
-         ],
-         "Select [%s]": [
-            ""
-         ],
-         "No data was returned.": [
-            "Keine Daten zurückgesendet"
-         ],
-         "List Druid Column": [
-            "Druid Spalten auflisten"
-         ],
-         "Show Druid Column": [
-            "Druid Spalte anzeigen"
-         ],
-         "Add Druid Column": [
-            "Druid Spalte einfügen"
-         ],
-         "Edit Druid Column": [
-            "Druid Spalte bearbeiten"
-         ],
-         "Column": [
-            "Spalte"
-         ],
-         "Type": [
-            "Typ"
-         ],
-         "Groupable": [
-            ""
-         ],
-         "Filterable": [
-            ""
-         ],
-         "Count Distinct": [
-            ""
-         ],
-         "Sum": [
-            ""
-         ],
-         "Whether this column is exposed in the `Filters` section of the explore view.": [
-            ""
-         ],
-         "List Druid Metric": [
-            "Druid Metriken auflisten"
-         ],
-         "Show Druid Metric": [
-            "Druid Metrik anzeigen"
-         ],
-         "Add Druid Metric": [
-            "Druid Metrik einfügen"
-         ],
-         "Edit Druid Metric": [
-            "Druid Metric bearbeiten"
-         ],
-         "Whether the access to this metric is restricted to certain roles. Only roles with the permission 'metric access on XXX (the name of this metric)' are allowed to access this metric": [
-            ""
-         ],
-         "Verbose Name": [
-            ""
-         ],
-         "JSON": [
-            "JSON"
-         ],
-         "Druid Datasource": [
-            "Druid Datenquelle"
-         ],
-         "Warning Message": [
-            "Warnmeldung"
-         ],
-         "List Druid Cluster": [
-            "Druid Cluster auflisten"
-         ],
-         "Show Druid Cluster": [
-            "Druid Cluster anzeigen"
-         ],
-         "Add Druid Cluster": [
-            "Druid Cluster einfügen"
-         ],
-         "Edit Druid Cluster": [
-            "Druid Cluster bearbeiten"
-         ],
-         "Cluster": [
-            ""
-         ],
-         "Broker Host": [
-            ""
-         ],
-         "Broker Port": [
-            ""
-         ],
-         "Broker Endpoint": [
-            ""
-         ],
-         "Druid Clusters": [
-            ""
-         ],
-         "Sources": [
-            "Quellen"
-         ],
-         "List Druid Datasource": [
-            "Druid Datenquellen auflisten"
-         ],
-         "Show Druid Datasource": [
-            "Druid Datenquelle anzeigen"
-         ],
-         "Add Druid Datasource": [
-            "Druid Datenquelle einfügen"
-         ],
-         "Edit Druid Datasource": [
-            "Druid Datenquelle bearbeiten"
-         ],
-         "The list of slices associated with this table. By altering this datasource, you may change how these associated slices behave. Also note that slices need to point to a datasource, so this form will fail at saving if removing slices from a datasource. If you want to change the datasource for a slice, overwrite the slice from the 'explore view'": [
-            ""
-         ],
-         "Timezone offset (in hours) for this datasource": [
-            ""
-         ],
-         "Time expression to use as a predicate when retrieving distinct values to populate the filter component. Only applies when `Enable Filter Select` is on. If you enter `7 days ago`, the distinct list of values in the filter will be populated based on the distinct value over the past week": [
-            ""
-         ],
-         "Whether to populate the filter's dropdown in the explore view's filter section with a list of distinct values fetched from the backend on the fly": [
-            ""
-         ],
-         "Redirects to this endpoint when clicking on the datasource from the datasource list": [
-            ""
-         ],
-         "Associated Slices": [
-            ""
-         ],
-         "Data Source": [
-            "Datenquelle"
-         ],
-         "Owner": [
-            ""
-         ],
-         "Is Hidden": [
-            ""
-         ],
-         "Enable Filter Select": [
-            ""
-         ],
-         "Default Endpoint": [
-            ""
-         ],
-         "Time Offset": [
-            ""
-         ],
-         "Cache Timeout": [
-            ""
-         ],
-         "Druid Datasources": [
-            "Druid Datenquellen"
-         ],
-         "Scan New Datasources": [
-            "Neue Datenquellen suchen"
-         ],
-         "Refresh Druid Metadata": [
-            "Druid Metadata aktualisieren"
-         ],
-         "Datetime column not provided as part table configuration and is required by this type of chart": [
-            ""
-         ],
-         "Empty query?": [
-            "Leere Abfrage?"
-         ],
-         "Metric '{}' is not valid": [
-            "Metrik '{}' ist nicht valide"
-         ],
-         "Table [{}] doesn't seem to exist in the specified database, couldn't fetch column information": [
-            ""
-         ],
-         "List Columns": [
-            "Spalten auflisten"
-         ],
-         "Show Column": [
-            "Spalte anzeigen"
-         ],
-         "Add Column": [
-            "Spalte einfügen"
-         ],
-         "Edit Column": [
-            "Spalte bearbeiten"
-         ],
-         "Whether to make this column available as a [Time Granularity] option, column has to be DATETIME or DATETIME-like": [
-            ""
-         ],
-         "The data type that was inferred by the database. It may be necessary to input a type manually for expression-defined columns in some cases. In most case users should not need to alter this.": [
-            ""
-         ],
-         "Expression": [
-            ""
-         ],
-         "Is temporal": [
-            ""
-         ],
-         "Datetime Format": [
-            ""
-         ],
-         "Database Expression": [
-            ""
-         ],
-         "List Metrics": [
-            "Metriken auflisten"
-         ],
-         "Show Metric": [
-            "Metrik anzeigen"
-         ],
-         "Add Metric": [
-            "Metrik einfügen"
-         ],
-         "Edit Metric": [
-            "Metrik bearbeiten"
-         ],
-         "SQL Expression": [
-            ""
-         ],
-         "D3 Format": [
-            ""
-         ],
-         "Is Restricted": [
-            "Ist begrenzt"
-         ],
-         "List Tables": [
-            "Tabellen auflisten"
-         ],
-         "Show Table": [
-            "Tabelle anzeigen"
-         ],
-         "Add Table": [
-            "Tabelle einfügen"
-         ],
-         "Edit Table": [
-            "Tabelle bearbeiten"
-         ],
-         "Name of the table that exists in the source database": [
-            ""
-         ],
-         "Schema, as used only in some databases like Postgres, Redshift and DB2": [
-            ""
-         ],
-         "This fields acts a Superset view, meaning that Superset will run a query against this string as a subquery.": [
-            ""
-         ],
-         "Predicate applied when fetching distinct value to populate the filter control component. Supports jinja template syntax. Applies only when `Enable Filter Select` is on.": [
-            ""
-         ],
-         "Redirects to this endpoint when clicking on the table from the table list": [
-            ""
-         ],
-         "Changed By": [
-            "Bearbeitet von"
-         ],
-         "Database": [
-            "Datenbank"
-         ],
-         "Last Changed": [
-            "Bearbeitet am"
-         ],
-         "Schema": [
-            ""
-         ],
-         "Offset": [
-            ""
-         ],
-         "Table Name": [
-            "Tabellenname"
-         ],
-         "Fetch Values Predicate": [
-            ""
-         ],
-         "Main Datetime Column": [
-            ""
-         ],
-         "Table [{}] could not be found, please double check your database connection, schema, and table name": [
-            ""
-         ],
-         "The table was created. As part of this two phase configuration process, you should now click the edit button by the new table to configure it.": [
-            ""
-         ],
-         "Tables": [
-            "Tabellen"
-         ],
-         "Profile": [
-            "Profil"
-         ],
-         "Logout": [
-            "Abmelden"
-         ],
-         "Login": [
-            "Anmelden"
-         ],
-         "Record Count": [
-            "Datensätzeanzahl"
-         ],
-         "No records found": [
-            "Keine Datensätze gefunden"
-         ],
-         "Import": [
-            "Importieren"
-         ],
-         "No Access!": [
-            "Keine Zugriff!"
-         ],
-         "You do not have permissions to access the datasource(s): %(name)s.": [
-            ""
-         ],
-         "Request Permissions": [
-            "Berechtigung anfordern"
-         ],
-         "Welcome!": [
-            "Willkommen!"
-         ],
-         "Test Connection": [
-            "Verbindungstest"
-         ],
-         "Manage": [
-            "Einstellungen"
-         ],
-         "Datasource %(name)s already exists": [
-            ""
-         ],
-         "json isn't valid": [
-            ""
-         ],
-         "Delete": [
-            "Löschen"
-         ],
-         "Delete all Really?": [
-            "Wirklich alle löschen?"
-         ],
-         "This endpoint requires the `all_datasource_access` permission": [
-            ""
-         ],
-         "The datasource seems to have been deleted": [
-            ""
-         ],
-         "The access requests seem to have been deleted": [
-            ""
-         ],
-         "The user seems to have been deleted": [
-            ""
-         ],
-         "You don't have access to this datasource": [
-            "Sie haben keine Zugriff auf diese Datenquelle"
-         ],
-         "This view requires the database %(name)s or `all_datasource_access` permission": [
-            ""
-         ],
-         "This endpoint requires the datasource %(name)s, database or `all_datasource_access` permission": [
-            ""
-         ],
-         "List Databases": [
-            "Dakenbanken auflisten"
-         ],
-         "Show Database": [
-            "Datenbank anzeigen"
-         ],
-         "Add Database": [
-            "Datenbank einfügen"
-         ],
-         "Edit Database": [
-            "Datenbank bearbeiten"
-         ],
-         "Expose this DB in SQL Lab": [
-            ""
-         ],
-         "Allow users to run synchronous queries, this is the default and should work well for queries that can be executed within a web request scope (<~1 minute)": [
-            ""
-         ],
-         "Allow users to run queries, against an async backend. This assumes that you have a Celery worker setup as well as a results backend.": [
-            ""
-         ],
-         "Allow CREATE TABLE AS option in SQL Lab": [
-            ""
-         ],
-         "Allow users to run non-SELECT statements (UPDATE, DELETE, CREATE, ...) in SQL Lab": [
-            ""
-         ],
-         "When allowing CREATE TABLE AS option in SQL Lab, this option forces the table to be created in this schema": [
-            ""
-         ],
-         "All the queries in Sql Lab are going to be executed on behalf of currently authorized user.": [
-            ""
-         ],
-         "Expose in SQL Lab": [
-            ""
-         ],
-         "Allow CREATE TABLE AS": [
-            ""
-         ],
-         "Allow DML": [
-            ""
-         ],
-         "CTAS Schema": [
-            ""
-         ],
-         "Creator": [
-            "Schöpfer"
-         ],
-         "SQLAlchemy URI": [
-            ""
-         ],
-         "Extra": [
-            ""
-         ],
-         "Allow Run Sync": [
-            ""
-         ],
-         "Allow Run Async": [
-            ""
-         ],
-         "Impersonate queries to the database": [
-            ""
-         ],
-         "Import Dashboards": [
-            "Dashboards importieren"
-         ],
-         "User": [
-            "Benutzer"
-         ],
-         "User Roles": [
-            "Benutzer Rollen"
-         ],
-         "Database URL": [
-            "Datenbank URL"
-         ],
-         "Roles to grant": [
-            ""
-         ],
-         "Created On": [
-            ""
-         ],
-         "Access requests": [
-            "Zugriffsanforderungen"
-         ],
-         "Security": [
-            "Sicherheit"
-         ],
-         "List Slices": [
-            "Schieben auflisten"
-         ],
-         "Show Slice": [
-            "Schiebe anzeigen"
-         ],
-         "Add Slice": [
-            "Schiebe einfügen"
-         ],
-         "Edit Slice": [
-            "Schiebe bearbeiten"
-         ],
-         "These parameters are generated dynamically when clicking the save or overwrite button in the explore view. This JSON object is exposed here for reference and for power users who may want to alter specific parameters.": [
-            ""
-         ],
-         "Duration (in seconds) of the caching timeout for this slice.": [
-            ""
-         ],
-         "Last Modified": [
-            "Geändert"
-         ],
-         "Owners": [
-            ""
-         ],
-         "Parameters": [
-            "Parameter"
-         ],
-         "Slice": [
-            "Schiebe"
-         ],
-         "List Dashboards": [
-            "Dashboards auflisten"
-         ],
-         "Show Dashboard": [
-            "Dashboard anzeigen"
-         ],
-         "Add Dashboard": [
-            "Dashboard einfügen"
-         ],
-         "Edit Dashboard": [
-            "Dashboard bearbeiten"
-         ],
-         "This json object describes the positioning of the widgets in the dashboard. It is dynamically generated when adjusting the widgets size and positions by using drag & drop in the dashboard view": [
-            ""
-         ],
-         "The css for individual dashboards can be altered here, or in the dashboard view where changes are immediately visible": [
-            ""
-         ],
-         "To get a readable URL for your dashboard": [
-            ""
-         ],
-         "This JSON object is generated dynamically when clicking the save or overwrite button in the dashboard view. It is exposed here for reference and for power users who may want to alter specific parameters.": [
-            ""
-         ],
-         "Owners is a list of users who can alter the dashboard.": [
-            ""
-         ],
-         "Dashboard": [
-            ""
-         ],
-         "Slug": [
-            ""
-         ],
-         "Position JSON": [
-            ""
-         ],
-         "JSON Metadata": [
-            ""
-         ],
-         "Underlying Tables": [
-            ""
-         ],
-         "Export": [
-            ""
-         ],
-         "Export dashboards?": [
-            ""
-         ],
-         "Action": [
-            "Aktion"
-         ],
-         "dttm": [
-            ""
-         ],
-         "Action Log": [
-            "Aktionsprotokoll"
-         ],
-         "Access was requested": [
-            "Zugang wurde beantragt"
-         ],
-         "%(user)s was granted the role %(role)s that gives access to the %(datasource)s": [
-            ""
-         ],
-         "Role %(r)s was extended to provide the access to the datasource %(ds)s": [
-            ""
-         ],
-         "You have no permission to approve this request": [
-            ""
-         ],
-         "Malformed request. slice_id or table_name and db_name arguments are expected": [
-            ""
-         ],
-         "Slice %(id)s not found": [
-            ""
-         ],
-         "Table %(t)s wasn't found in the database %(d)s": [
-            ""
-         ],
-         "Can't find User '%(name)s', please ask your admin to create one.": [
-            ""
-         ],
-         "Can't find DruidCluster with cluster_name = '%(name)s'": [
-            ""
-         ],
-         "Query record was not created as expected.": [
-            ""
-         ],
-         "Template Name": [
-            "Vorlagename"
-         ],
-         "CSS Templates": [
-            "CSS Vorlagen"
-         ],
-         "SQL Editor": [
-            ""
-         ],
-         "SQL Lab": [
-            ""
-         ],
-         "Query Search": [
-            "Abfragen suchen"
-         ],
-         "Status": [
-            ""
-         ],
-         "Start Time": [
-            "Von Zeit"
-         ],
-         "End Time": [
-            "Bis Zeit"
-         ],
-         "Queries": [
-            "Abfragen"
-         ],
-         "List Saved Query": [
-            "Gespeicherte Abfragen auflisten"
-         ],
-         "Show Saved Query": [
-            "Gespeicherte Abfrage anzeigen"
-         ],
-         "Add Saved Query": [
-            "Gespeicherte Abfrage einfügen"
-         ],
-         "Edit Saved Query": [
-            "Gespeicherte Abfrage bearbeiten"
-         ],
-         "Pop Tab Link": [
-            ""
-         ],
-         "Changed on": [
-            "Bearbeitet am"
-         ],
-         "Saved Queries": [
-            "Gespeicherte Abfragen"
-         ]
-      }
-   }
-}
\ No newline at end of file
+{"domain":"superset","locale_data":{"superset":{"":{"domain":"superset","plural_forms":"nplurals=2; plural=(n != 1)","lang":"de"},"Time Column":["Zeitspalte"],"second":["Sekunde"],"minute":["Minute"],"hour":["Stunde"],"day":["Tag"],"week":["Woche"],"month":["Monat"],"quarter":["Vierteljahr"],"year":["Jahr"],"week_start_monday":[""],"week_ending_saturday":[""],"week_start_sunday":[""],"5 minute":["5 Minuten"],"half hour":["halbe Stunde"],"10 minute":["10 Minuten"],"[Superset] Access to th [...]
+ to new dashboard":["Schiebe zum neuen Dashboard hinzufügen"],"Save & go to dashboard":["Speichern & zum Dashboard gehen"],"Check out this slice: %s":[""],"`Min` value should be numeric or empty":[""],"`Max` value should be numeric or empty":[""],"Min":[""],"Max":[""],"Something went wrong while fetching the datasource list":[""],"Click to point to another datasource":[""],"Edit the datasource's configuration":[""],"Select a datasource":[""],"Search / Filter":[""],"Filter value":[""],"Se [...]
+ rich tooltip shows a list of all series for that point in time":[""],"Y Log Scale":[""],"Use a log scale for the Y axis":[""],"X Log Scale":[""],"Use a log scale for the X axis":[""],"Donut":[""],"Do you want a donut or a pie?":[""],"Put labels outside":[""],"Put the labels outside the pie?":[""],"Contribution":[""],"Compute the contribution to the total":[""],"Period Ratio":[""],"[integer] Number of period to compare against, this is relative to the granularity selected":[""],"Period R [...]
+ explore view's filter section with a list of distinct values fetched from the backend on the fly":[""],"Redirects to this endpoint when clicking on the datasource from the datasource list":[""],"Associated Slices":[""],"Data Source":["Datenquelle"],"Owner":[""],"Is Hidden":[""],"Enable Filter Select":[""],"Default Endpoint":[""],"Time Offset":[""],"Cache Timeout":[""],"Druid Datasources":["Druid Datenquellen"],"Scan New Datasources":["Neue Datenquellen suchen"],"Refresh Druid Metadata": [...]
diff --git a/superset/translations/de/LC_MESSAGES/messages.mo b/superset/translations/de/LC_MESSAGES/messages.mo
index 7f6c922..0d51828 100644
Binary files a/superset/translations/de/LC_MESSAGES/messages.mo and b/superset/translations/de/LC_MESSAGES/messages.mo differ
diff --git a/superset/translations/de/LC_MESSAGES/messages.po b/superset/translations/de/LC_MESSAGES/messages.po
index 45df937..7e0ea5a 100644
--- a/superset/translations/de/LC_MESSAGES/messages.po
+++ b/superset/translations/de/LC_MESSAGES/messages.po
@@ -161,7 +161,7 @@ msgstr ""
 #: superset/assets/javascripts/explore/stores/visTypes.js:386
 #: superset/viz.py:430
 msgid "Pivot Table"
-msgstr ""
+msgstr "Pivot-Tabelle"
 
 #: superset/viz.py:444
 msgid "Please choose at least one \"Group by\" field "
@@ -169,7 +169,7 @@ msgstr ""
 
 #: superset/viz.py:446
 msgid "Please choose at least one metric"
-msgstr ""
+msgstr "Bitte wählt zumindest eine Metrik"
 
 #: superset/viz.py:450
 msgid "'Group By' and 'Columns' can't overlap"
@@ -178,7 +178,7 @@ msgstr ""
 #: superset/assets/javascripts/explore/stores/visTypes.js:373
 #: superset/viz.py:483
 msgid "Markup"
-msgstr ""
+msgstr "Auszeichnung"
 
 #: superset/assets/javascripts/explore/stores/visTypes.js:411
 #: superset/viz.py:502
@@ -188,7 +188,7 @@ msgstr ""
 #: superset/assets/javascripts/explore/stores/visTypes.js:433
 #: superset/viz.py:514
 msgid "Word Cloud"
-msgstr ""
+msgstr "Wortwolke"
 
 #: superset/assets/javascripts/explore/stores/visTypes.js:454
 #: superset/viz.py:537
@@ -203,7 +203,7 @@ msgstr ""
 #: superset/assets/javascripts/explore/stores/visTypes.js:502
 #: superset/viz.py:621
 msgid "Box Plot"
-msgstr ""
+msgstr "Box-Plot"
 
 #: superset/assets/javascripts/explore/stores/visTypes.js:523
 #: superset/viz.py:710
@@ -230,12 +230,12 @@ msgstr ""
 
 #: superset/viz.py:817 superset/viz.py:846
 msgid "Pick a metric!"
-msgstr ""
+msgstr "Wählt eine Metrik!"
 
 #: superset/assets/javascripts/explore/stores/visTypes.js:622
 #: superset/viz.py:838
 msgid "Big Number"
-msgstr ""
+msgstr "Große Nummer"
 
 #: superset/assets/javascripts/explore/stores/visTypes.js:157
 #: superset/viz.py:865
diff --git a/superset/translations/zh/LC_MESSAGES/messages.mo b/superset/translations/zh/LC_MESSAGES/messages.mo
index 51e42a0..31255f8 100644
Binary files a/superset/translations/zh/LC_MESSAGES/messages.mo and b/superset/translations/zh/LC_MESSAGES/messages.mo differ
diff --git a/superset/translations/zh/LC_MESSAGES/messages.po b/superset/translations/zh/LC_MESSAGES/messages.po
index a481407..8e0186e 100644
--- a/superset/translations/zh/LC_MESSAGES/messages.po
+++ b/superset/translations/zh/LC_MESSAGES/messages.po
@@ -214,7 +214,7 @@ msgid "Schema"
 msgstr "模式"
 
 #: superset/forms.py:86
-msgid "Specify a schema (if database flavour supports this)."
+msgid "Specify a schema (if database flavor supports this)."
 msgstr "指定一个Schema(需要数据库支持)"
 
 #: superset/forms.py:91
@@ -4613,19 +4613,17 @@ msgstr "您无法访问此数据源。< a href = ‘{}’> (获取权限) </a>"
 msgid "You don't have access to this datasource"
 msgstr "你不能访问这个数据源"
 
-#: superset/views/core.py:90
+#: superset/views/core.py:98
 #, python-format
 msgid ""
-"This view requires the database %(name)s or `all_datasource_access` "
-"permission"
+"This view requires the database %(name)s or "
+"`all_datasource_access` permission"
 msgstr "此视图需要数据库 %(name)s或“all_datasource_access”权限"
 
-#: superset/views/core.py:95
+#: superset/views/core.py:82
 #, python-format
-msgid ""
-"This endpoint requires the datasource %(name)s, database or "
-"`all_datasource_access` permission"
-msgstr "此端点需要数据源 %(name)s、数据库或“all_datasource_access”权限"
+msgid "This endpoint requires the `all_datasource_access` permission"
+msgstr "此端点需要“all_datasource_access”权限"
 
 #: superset/views/core.py:189
 msgid "List Databases"
@@ -4853,10 +4851,11 @@ msgstr ""
 "这个JSON对象描述了部件在看板中的位置。它是动态生成的,可以通过拖放,在看板中"
 "调整整部件的大小和位置"
 
-#: superset/views/core.py:552
+#: superset/views/core.py:627
 msgid ""
-"The css for individual dashboards can be altered here, or in the dashboard "
-"view where changes are immediately visible"
+"The CSS for individual dashboards can be altered here, or "
+"in the dashboard view where changes are immediately "
+"visible"
 msgstr "可以在这里或者在看板视图修改单个看板的CSS样式"
 
 #: superset/views/core.py:556
@@ -5054,6 +5053,83 @@ msgstr "改变为"
 msgid "Saved Queries"
 msgstr "已保存查询"
 
+#: superset/views/core.py:299
+msgid "Chart Cache Timeout"
+msgstr "表缓存超时"
+
+#: superset/views/core.py:284
+msgid ""
+"Duration (in seconds) of the caching timeout for charts of this database. "
+"A timeout of 0 indicates that the cache never expires. "
+"Note this defaults to the global timeout if undefined."
+msgstr ""
+"此数据库图表的缓存超时持续时间(以秒为单位)。超时为0表示缓存永远不会过期。"
+"注意,如果未定义,这默认为全局超时。"
+
+#: superset/views/core.py:303
+msgid "Allow Csv Upload"
+msgstr "允许Csv上传"
+
+#: superset/views/core.py:288
+msgid "If selected, please set the schemas allowed for csv upload in Extra."
+msgstr "如果选择,请额外设置csv上传允许的模式。"
+
+#: superset/views/core.py:301
+msgid "Asynchronous Query Execution"
+msgstr "异步执行查询"
+
+#: superset/views/core.py:237
+msgid ""
+"Operate the database in asynchronous mode, meaning  "
+"that the queries are executed on remote workers as opposed "
+"to on the web server itself. "
+"This assumes that you have a Celery worker setup as well "
+"as a results backend. Refer to the installation docs "
+"for more information."
+msgstr ""
+"以异步模式操作数据库,这意味着查询是在远程工作人员上执行的,而不是在web服务器本身上执行的, "
+"这假设您有一个Celery worker setup以及一个结果后端。有关更多信息,请参考安装文档。"
+
+#: superset/connectors/sqla/views.py:173
+msgid "Import a table definition"
+msgstr "导入一个已定义的表"
+
+#: superset/connectors/sqla/views.py:237
+msgid ""
+"Duration (in seconds) of the caching timeout for this table. "
+"A timeout of 0 indicates that the cache never expires. "
+"Note this defaults to the database timeout if undefined."
+msgstr ""
+"此表的缓存超时持续时间(以秒为单位)。超时为0表示缓存永远不会过期。"
+"注意,如果未定义,这默认为数据库的超时。"
+
+#: superset/views/core.py:520
+msgid ""
+"Duration (in seconds) of the caching timeout for this chart. "
+"Note this defaults to the datasource/table timeout if undefined."
+msgstr ""
+"此图表的缓存超时持续时间(以秒为单位)。"
+"注意,如果未定义,这默认为数据源/表超时。"
+
+#: superset/connectors/druid/views.py:189
+msgid ""
+"Duration (in seconds) of the caching timeout for this cluster. "
+"A timeout of 0 indicates that the cache never expires. "
+"Note this defaults to the global timeout if undefined."
+msgstr ""
+"此集群的缓存超时持续时间(以秒为单位)。超时为0表示缓存永远不会过期。"
+"注意,如果未定义,这默认为全局超时。"
+
+#: superset/connectors/druid/views.py:276
+msgid ""
+"Duration (in seconds) of the caching timeout for this datasource. "
+"A timeout of 0 indicates that the cache never expires. "
+"Note this defaults to the cluster timeout if undefined."
+msgstr ""
+"此数据源的缓存超时持续时间(以秒为单位)。超时为0表示缓存永远不会过期。"
+"注意,如果未定义,这默认为集群超时。"
+
+
 #~ msgid "Please choose at least one \"Group by\" field"
 #~ msgstr "请至少选择一个“分组”字段"
 
diff --git a/superset/utils/core.py b/superset/utils/core.py
index 122998e..3b41457 100644
--- a/superset/utils/core.py
+++ b/superset/utils/core.py
@@ -33,6 +33,7 @@ import smtplib
 import sys
 from time import struct_time
 from typing import List, Optional, Tuple
+from urllib.parse import unquote_plus
 import uuid
 import zlib
 
@@ -141,8 +142,18 @@ def memoized(func=None, watch=None):
         return wrapper
 
 
-def js_string_to_python(item: str) -> Optional[str]:
-    return None if item in ('null', 'undefined') else item
+def parse_js_uri_path_item(item: Optional[str], unquote: bool = True,
+                           eval_undefined: bool = False) -> Optional[str]:
+    """Parse a uri path item made with js.
+
+    :param item: a uri path component
+    :param unquote: Perform unquoting of string using urllib.parse.unquote_plus()
+    :param eval_undefined: When set to True and item is either 'null'  or 'undefined',
+    assume item is undefined and return None.
+    :return: Either None, the original item or unquoted item
+    """
+    item = None if eval_undefined and item in ('null', 'undefined') else item
+    return unquote_plus(item) if unquote and item else item
 
 
 def string_to_num(s: str):
@@ -852,11 +863,6 @@ def merge_request_params(form_data: dict, params: dict):
     form_data['url_params'] = url_params
 
 
-def get_update_perms_flag() -> bool:
-    val = os.environ.get('SUPERSET_UPDATE_PERMS')
-    return val.lower() not in ('0', 'false', 'no') if val else True
-
-
 def user_label(user: User) -> Optional[str]:
     """Given a user ORM FAB object, returns a label"""
     if user:
diff --git a/superset/views/annotations.py b/superset/views/annotations.py
index 8177efc..9ef2d65 100644
--- a/superset/views/annotations.py
+++ b/superset/views/annotations.py
@@ -18,12 +18,30 @@
 from flask_appbuilder.models.sqla.interface import SQLAInterface
 from flask_babel import gettext as __
 from flask_babel import lazy_gettext as _
+from wtforms.validators import StopValidation
 
 from superset import appbuilder
 from superset.models.annotations import Annotation, AnnotationLayer
 from .base import DeleteMixin, SupersetModelView
 
 
+class StartEndDttmValidator(object):
+    """
+    Validates dttm fields.
+    """
+    def __call__(self, form, field):
+        if not form['start_dttm'].data and not form['end_dttm'].data:
+            raise StopValidation(
+                _('annotation start time or end time is required.'),
+            )
+        elif (form['end_dttm'].data and
+                form['start_dttm'].data and
+                form['end_dttm'].data < form['start_dttm'].data):
+            raise StopValidation(
+                _('Annotation end time must be no earlier than start time.'),
+            )
+
+
 class AnnotationModelView(SupersetModelView, DeleteMixin):  # noqa
     datamodel = SQLAInterface(Annotation)
 
@@ -53,17 +71,17 @@ class AnnotationModelView(SupersetModelView, DeleteMixin):  # noqa
          annotation needs to add more context.',
     }
 
+    validators_columns = {
+        'start_dttm': [
+            StartEndDttmValidator(),
+        ],
+    }
+
     def pre_add(self, obj):
-        if not obj.layer:
-            raise Exception('Annotation layer is required.')
-        if not obj.start_dttm and not obj.end_dttm:
-            raise Exception('Annotation start time or end time is required.')
-        elif not obj.start_dttm:
+        if not obj.start_dttm:
             obj.start_dttm = obj.end_dttm
         elif not obj.end_dttm:
             obj.end_dttm = obj.start_dttm
-        elif obj.end_dttm < obj.start_dttm:
-            raise Exception('Annotation end time must be no earlier than start time.')
 
     def pre_update(self, obj):
         self.pre_add(obj)
diff --git a/superset/views/core.py b/superset/views/core.py
index a7188d2..1ccfa10 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -1566,8 +1566,8 @@ class Superset(BaseSupersetView):
         """Endpoint to fetch the list of tables for given database"""
         db_id = int(db_id)
         force_refresh = force_refresh.lower() == 'true'
-        schema = utils.js_string_to_python(schema)
-        substr = utils.js_string_to_python(substr)
+        schema = utils.parse_js_uri_path_item(schema, eval_undefined=True)
+        substr = utils.parse_js_uri_path_item(substr, eval_undefined=True)
         database = db.session.query(models.Database).filter_by(id=db_id).one()
 
         if schema:
@@ -2350,11 +2350,11 @@ class Superset(BaseSupersetView):
         }))
 
     @has_access
-    @expose('/table/<database_id>/<path:table_name>/<schema>/')
+    @expose('/table/<database_id>/<table_name>/<schema>/')
     @log_this
     def table(self, database_id, table_name, schema):
-        schema = utils.js_string_to_python(schema)
-        table_name = parse.unquote_plus(table_name)
+        schema = utils.parse_js_uri_path_item(schema, eval_undefined=True)
+        table_name = utils.parse_js_uri_path_item(table_name)
         mydb = db.session.query(models.Database).filter_by(id=database_id).one()
         payload_columns = []
         indexes = []
@@ -2410,11 +2410,11 @@ class Superset(BaseSupersetView):
         return json_success(json.dumps(tbl))
 
     @has_access
-    @expose('/extra_table_metadata/<database_id>/<path:table_name>/<schema>/')
+    @expose('/extra_table_metadata/<database_id>/<table_name>/<schema>/')
     @log_this
     def extra_table_metadata(self, database_id, table_name, schema):
-        schema = utils.js_string_to_python(schema)
-        table_name = parse.unquote_plus(table_name)
+        schema = utils.parse_js_uri_path_item(schema, eval_undefined=True)
+        table_name = utils.parse_js_uri_path_item(table_name)
         mydb = db.session.query(models.Database).filter_by(id=database_id).one()
         payload = mydb.db_engine_spec.extra_table_metadata(
             mydb, table_name, schema)
@@ -2427,6 +2427,8 @@ class Superset(BaseSupersetView):
     def select_star(self, database_id, table_name, schema=None):
         mydb = db.session.query(
             models.Database).filter_by(id=database_id).first()
+        schema = utils.parse_js_uri_path_item(schema, eval_undefined=True)
+        table_name = utils.parse_js_uri_path_item(table_name)
         return json_success(
             mydb.select_star(
                 table_name,
@@ -2577,8 +2579,9 @@ class Superset(BaseSupersetView):
         except Exception as e:
             logging.exception(e)
             msg = _(
-                f'{validator.name} was unable to check your query.\nPlease '
-                'make sure that any services it depends on are available\n'
+                'Failed to validate your SQL query text. Please check that '
+                f'you have configured the {validator.name} validator '
+                'correctly and that any services it depends on are up. '
                 f'Exception: {e}')
             return json_error_response(f'{msg}')
 
diff --git a/superset/viz.py b/superset/viz.py
index 123c361..9661e23 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -213,7 +213,7 @@ class BaseViz(object):
                     try:
                         int(one_ts_val)
                         is_integral = True
-                    except ValueError:
+                    except (ValueError, TypeError):
                         is_integral = False
                     if is_integral:
                         unit = 's' if timestamp_format == 'epoch_s' else 'ms'
diff --git a/tests/security_tests.py b/tests/security_tests.py
index 57b790c..6912dee 100644
--- a/tests/security_tests.py
+++ b/tests/security_tests.py
@@ -249,6 +249,8 @@ class RolePermissionTests(SupersetTestCase):
             ['Superset', 'log'],
             ['Superset', 'theme'],
             ['Superset', 'welcome'],
+            ['SecurityApi', 'login'],
+            ['SecurityApi', 'refresh'],
         ]
         unsecured_views = []
         for view_class in appbuilder.baseviews:
diff --git a/tests/sql_parse_tests.py b/tests/sql_parse_tests.py
index 5695939..7096147 100644
--- a/tests/sql_parse_tests.py
+++ b/tests/sql_parse_tests.py
@@ -462,3 +462,12 @@ class SupersetTestCase(unittest.TestCase):
             'SELECT * FROM ab_user LIMIT 1',
         ]
         self.assertEquals(statements, expected)
+
+    def test_identifier_list_with_keyword_as_alias(self):
+        query = """
+        WITH
+            f AS (SELECT * FROM foo),
+            match AS (SELECT * FROM f)
+        SELECT * FROM match
+        """
+        self.assertEquals({'foo'}, self.extract_tables(query))
diff --git a/tests/utils_tests.py b/tests/utils_tests.py
index 7e29089..40dedaf 100644
--- a/tests/utils_tests.py
+++ b/tests/utils_tests.py
@@ -35,6 +35,7 @@ from superset.utils.core import (
     merge_extra_filters,
     merge_request_params,
     parse_human_timedelta,
+    parse_js_uri_path_item,
     validate_json,
     zlib_compress,
     zlib_decompress_to_string,
@@ -756,3 +757,18 @@ class UtilsTestCase(unittest.TestCase):
         }
         convert_legacy_filters_into_adhoc(form_data)
         self.assertEquals(form_data, expected)
+
+    def test_parse_js_uri_path_items_eval_undefined(self):
+        self.assertIsNone(parse_js_uri_path_item('undefined', eval_undefined=True))
+        self.assertIsNone(parse_js_uri_path_item('null', eval_undefined=True))
+        self.assertEqual('undefined', parse_js_uri_path_item('undefined'))
+        self.assertEqual('null', parse_js_uri_path_item('null'))
+
+    def test_parse_js_uri_path_items_unquote(self):
+        self.assertEqual('slashed/name', parse_js_uri_path_item('slashed%2fname'))
+        self.assertEqual('slashed%2fname', parse_js_uri_path_item('slashed%2fname',
+                                                                  unquote=False))
+
+    def test_parse_js_uri_path_items_item_optional(self):
+        self.assertIsNone(parse_js_uri_path_item(None))
+        self.assertIsNotNone(parse_js_uri_path_item('item'))


Mime
View raw message