From e42ae92cadea3467dbbbc5efc218ac58f1947e3f Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Mon, 27 Feb 2017 13:58:05 -0800 Subject: [PATCH 1/2] [table viz] allow showing time granularity in table (#2284) The time granularity currently does not show up in table viz. Now defining a granularity will add an extra ISO-formatted time column in the table. The column will be added for both grouped by and not grouped by. --- .../explorev2/components/controls/Filter.jsx | 18 ++++++++++++--- superset/models.py | 23 +++++++++++-------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/superset/assets/javascripts/explorev2/components/controls/Filter.jsx b/superset/assets/javascripts/explorev2/components/controls/Filter.jsx index 225c832a1f64b..29c69d165b51b 100644 --- a/superset/assets/javascripts/explorev2/components/controls/Filter.jsx +++ b/superset/assets/javascripts/explorev2/components/controls/Filter.jsx @@ -4,6 +4,9 @@ import Select from 'react-select'; import { Button, Row, Col } from 'react-bootstrap'; import SelectControl from './SelectControl'; +const arrayFilterOps = ['in', 'not in']; +const strFilterOps = ['==', '!=', '>', '<', '>=', '<=', 'regex']; + const propTypes = { choices: PropTypes.array, changeFilter: PropTypes.func, @@ -55,6 +58,15 @@ export default class Filter extends React.Component { if (event && event.value) { value = event.value; } + if (control === 'op') { + if (arrayFilterOps.indexOf(this.props.filter.op) !== -1 + && strFilterOps.indexOf(value) !== -1) { + this.props.changeFilter('val', this.props.filter.val[0]); + } else if (strFilterOps.indexOf(this.props.filter.op) !== -1 + && arrayFilterOps.indexOf(value) !== -1) { + this.props.changeFilter('val', [this.props.filter.val]); + } + } this.props.changeFilter(control, value); if (control === 'col' && value !== null && this.props.datasource.filter_select) { this.fetchFilterValues(value); @@ -70,13 +82,13 @@ export default class Filter extends React.Component { this.fetchFilterValues(filter.col); } } - if (this.props.having) { - // druid having filter + if (this.props.having || strFilterOps.indexOf(filter.op) !== -1) { + // druid having filter or regex/==/!= filters return ( diff --git a/superset/models.py b/superset/models.py index cf6b1658b39d7..cd3ed4d25b907 100644 --- a/superset/models.py +++ b/superset/models.py @@ -1403,7 +1403,8 @@ def visit_column(element, compiler, **kw): col_obj = cols.get(col) if col_obj and op in ('in', 'not in'): values = [types.strip("'").strip('"') for types in eq] - values = [utils.js_string_to_num(s) for s in values] + if col_obj.is_num: + values = [utils.js_string_to_num(s) for s in values] cond = col_obj.sqla_col.in_(values) if op == 'not in': cond = ~cond @@ -2567,8 +2568,7 @@ def increment_timestamp(ts): query=query_str, duration=datetime.now() - qry_start_dttm) - @staticmethod - def get_filters(raw_filters): + def get_filters(self, raw_filters): filters = None for flt in raw_filters: if not all(f in flt for f in ['col', 'op', 'val']): @@ -2577,21 +2577,24 @@ def get_filters(raw_filters): op = flt['op'] eq = flt['val'] cond = None + if op in ('in', 'not in'): + eq = [types.replace("'", '').strip() for types in eq] + if col in self.num_cols: + if op in ('in', 'not in'): + eq = [utils.js_string_to_num(v) for v in eq] + else: + eq = utils.js_string_to_num(eq) if op == '==': cond = Dimension(col) == eq elif op == '!=': cond = ~(Dimension(col) == eq) elif op in ('in', 'not in'): fields = [] - # Distinguish quoted values with regular value types - values = [types.replace("'", '') for types in eq] - values = [utils.js_string_to_num(s) for s in values] - if len(values) > 1: - for s in values: - s = s.strip() + if len(eq) > 1: + for s in eq: fields.append(Dimension(col) == s) cond = Filter(type="or", fields=fields) - elif len(values) == 1: + elif len(eq) == 1: cond = Dimension(col) == eq[0] if op == 'not in': cond = ~cond From b17fa85eacc1f6dadf333c5e744954894856ed35 Mon Sep 17 00:00:00 2001 From: Vera Liu Date: Mon, 27 Feb 2017 21:29:18 -0800 Subject: [PATCH 2/2] Changes based on comments --- .../explorev2/components/controls/Filter.jsx | 31 ++++++++++++++----- .../components/controls/FilterControl.jsx | 8 ++++- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/superset/assets/javascripts/explorev2/components/controls/Filter.jsx b/superset/assets/javascripts/explorev2/components/controls/Filter.jsx index 29c69d165b51b..263b29f9e804e 100644 --- a/superset/assets/javascripts/explorev2/components/controls/Filter.jsx +++ b/superset/assets/javascripts/explorev2/components/controls/Filter.jsx @@ -50,6 +50,21 @@ export default class Filter extends React.Component { }); } } + switchFilterValue(prevFilter, nextOp) { + const prevOp = prevFilter.op; + let newVal = null; + if (arrayFilterOps.indexOf(prevOp) !== -1 + && strFilterOps.indexOf(nextOp) !== -1) { + // switch from array to string + newVal = this.props.filter.val.length > 0 ? this.props.filter.val[0] : ''; + } + if (strFilterOps.indexOf(prevOp) !== -1 + && arrayFilterOps.indexOf(nextOp) !== -1) { + // switch from string to array + newVal = this.props.filter.val === '' ? [] : [this.props.filter.val]; + } + return newVal; + } changeFilter(control, event) { let value = event; if (event && event.target) { @@ -59,15 +74,15 @@ export default class Filter extends React.Component { value = event.value; } if (control === 'op') { - if (arrayFilterOps.indexOf(this.props.filter.op) !== -1 - && strFilterOps.indexOf(value) !== -1) { - this.props.changeFilter('val', this.props.filter.val[0]); - } else if (strFilterOps.indexOf(this.props.filter.op) !== -1 - && arrayFilterOps.indexOf(value) !== -1) { - this.props.changeFilter('val', [this.props.filter.val]); + const newVal = this.switchFilterValue(this.props.filter, value); + if (newVal) { + this.props.changeFilter(['op', 'val'], [value, newVal]); + } else { + this.props.changeFilter(control, value); } + } else { + this.props.changeFilter(control, value); } - this.props.changeFilter(control, value); if (control === 'col' && value !== null && this.props.datasource.filter_select) { this.fetchFilterValues(value); } @@ -82,7 +97,7 @@ export default class Filter extends React.Component { this.fetchFilterValues(filter.col); } } - if (this.props.having || strFilterOps.indexOf(filter.op) !== -1) { + if (strFilterOps.indexOf(filter.op) !== -1) { // druid having filter or regex/==/!= filters return ( { + modifiedFilter[c] = value[i]; + }); + } newFilters.splice(index, 1, modifiedFilter); this.props.onChange(newFilters); }