diff --git a/superset/assets/javascripts/SqlLab/actions.js b/superset/assets/javascripts/SqlLab/actions.js index 2e60a4c8ba81b..0effacefcf1a5 100644 --- a/superset/assets/javascripts/SqlLab/actions.js +++ b/superset/assets/javascripts/SqlLab/actions.js @@ -151,6 +151,9 @@ export function runQuery(query) { } else if (msg === null) { msg = `[${textStatus}] ${errorThrown}`; } + if (msg.indexOf('The CSRF token is missing') > 0) { + msg = 'Your session timed out, please refresh your page and try again.'; + } dispatch(queryFailed(query, msg)); }, }); diff --git a/superset/assets/javascripts/SqlLab/components/ResultSet.jsx b/superset/assets/javascripts/SqlLab/components/ResultSet.jsx index f6661823fcc69..bee97d61a27e9 100644 --- a/superset/assets/javascripts/SqlLab/components/ResultSet.jsx +++ b/superset/assets/javascripts/SqlLab/components/ResultSet.jsx @@ -38,6 +38,10 @@ export default class ResultSet extends React.PureComponent { height: props.search ? props.height - RESULT_SET_CONTROLS_HEIGHT : props.height, }; } + componentDidMount() { + // only do this the first time the component is rendered/mounted + this.reRunQueryIfSessionTimeoutErrorOnMount(); + } componentWillReceiveProps(nextProps) { // when new results comes in, save them locally and clear in store if (this.props.cache && (!nextProps.query.cached) @@ -53,7 +57,6 @@ export default class ResultSet extends React.PureComponent { this.fetchResults(nextProps.query); } } - getControls() { if (this.props.search || this.props.visualize || this.props.csv) { let csvButton; @@ -132,7 +135,12 @@ export default class ResultSet extends React.PureComponent { reFetchQueryResults(query) { this.props.actions.reFetchQueryResults(query); } - + reRunQueryIfSessionTimeoutErrorOnMount() { + const { query } = this.props; + if (query.errorMessage && query.errorMessage.indexOf('session timed out') > 0) { + this.props.actions.runQuery(query, true); + } + } render() { const query = this.props.query; const results = query.results; diff --git a/superset/assets/package.json b/superset/assets/package.json index 56b0738faf309..d586fe8c075fe 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -1,6 +1,6 @@ { "name": "superset", - "version": "0.17.6-alpha.1", + "version": "0.17.6", "description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.", "license": "Apache-2.0", "directories": { diff --git a/superset/assets/visualizations/nvd3_vis.js b/superset/assets/visualizations/nvd3_vis.js index 6bc45e401992d..aa49d437f560b 100644 --- a/superset/assets/visualizations/nvd3_vis.js +++ b/superset/assets/visualizations/nvd3_vis.js @@ -23,8 +23,8 @@ const BREAKPOINTS = { small: 340, }; -const addTotalBarValues = function (svg, chart, data, stacked) { - const format = d3.format('.3s'); +const addTotalBarValues = function (svg, chart, data, stacked, axisFormat) { + const format = d3.format(axisFormat || '.3s'); const countSeriesDisplayed = data.length; const totalStackedValues = stacked && data.length !== 0 ? @@ -169,7 +169,7 @@ function nvd3Vis(slice, payload) { if (fd.show_bar_value) { setTimeout(function () { - addTotalBarValues(svg, chart, payload.data, stacked); + addTotalBarValues(svg, chart, payload.data, stacked, fd.y_axis_format); }, animationTime); } break; @@ -199,7 +199,7 @@ function nvd3Vis(slice, payload) { } if (fd.show_bar_value) { setTimeout(function () { - addTotalBarValues(svg, chart, payload.data, stacked); + addTotalBarValues(svg, chart, payload.data, stacked, fd.y_axis_format); }, animationTime); } if (!reduceXTicks) { diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py index 1ed45023fbc97..913ee595793f8 100644 --- a/superset/connectors/sqla/models.py +++ b/superset/connectors/sqla/models.py @@ -1,6 +1,7 @@ from datetime import datetime import logging import sqlparse +from past.builtins import basestring import pandas as pd @@ -177,6 +178,11 @@ class SqlaTable(Model, BaseDatasource): foreign_keys=[database_id]) schema = Column(String(255)) sql = Column(Text) + slices = relationship( + 'Slice', + primaryjoin=( + "SqlaTable.id == foreign(Slice.datasource_id) and " + "Slice.datasource_type == 'table'")) baselink = "tablemodelview" export_fields = ( @@ -462,9 +468,19 @@ def visit_column(element, compiler, **kw): col_obj = cols.get(col) if col_obj: if op in ('in', 'not in'): - values = [types.strip("'").strip('"') for types in eq] - if col_obj.is_num: - values = [utils.js_string_to_num(s) for s in values] + values = [] + for v in eq: + # For backwards compatibility and edge cases + # where a column data type might have changed + if isinstance(v, basestring): + v = v.strip("'").strip('"') + if col_obj.is_num: + v = utils.string_to_num(v) + + # Removing empty strings and non numeric values + # targeting numeric columns + if v is not None: + values.append(v) cond = col_obj.sqla_col.in_(values) if op == 'not in': cond = ~cond