Skip to content

Commit

Permalink
Put filters in an array instead of flt strings
Browse files Browse the repository at this point in the history
  • Loading branch information
vera-liu committed Feb 1, 2017
1 parent c8a00e6 commit 9fdc543
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 104 deletions.
36 changes: 36 additions & 0 deletions superset/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from superset.source_registry import SourceRegistry
from werkzeug.contrib.fixers import ProxyFix
from superset import utils
import re


APP_DIR = os.path.dirname(__file__)
Expand All @@ -39,6 +40,41 @@ def cast_form_data(form_data):
d[k] = v
return d

def cast_filter_data(form_data):
flts = []
having_flts = []
fd = form_data
filter_pattern = re.compile(r'''((?:[^,"']|"[^"]*"|'[^']*')+)''')
for i in range(0, 10):
for prefix in ['flt', 'having']:
col_str = '{}_col_{}'.format(prefix, i)
op_str = '{}_op_{}'.format(prefix, i)
val_str = '{}_eq_{}'.format(prefix, i)
if col_str in fd and op_str in fd and val_str in fd \
and len(fd[val_str]) > 0:
f = {}
f['col'] = fd[col_str]
f['op'] = fd[op_str]
if prefix == 'flt':
# transfer old strings in filter value to list
splitted = filter_pattern.split(fd[val_str])[1::2]
values = [types.replace("'", '').strip() for types in splitted]
f['val'] = values
flts.append(f)
if prefix == 'having':
f['val'] = fd[val_str]
having_flts.append(f)
if col_str in fd:
del fd[col_str]
if op_str in fd:
del fd[op_str]
if val_str in fd:
del fd[val_str]
fd['filters'] = flts
fd['having_filters'] = having_flts
return fd


app = Flask(__name__)
app.config.from_object(CONFIG_MODULE)
conf = app.config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class ControlPanelsContainer extends React.Component {
value={this.props.form_data[fieldName]}
validationErrors={this.props.fields[fieldName].validationErrors}
actions={this.props.actions}
prefix={section.prefix}
{...this.getFieldData(fieldName)}
/>
))}
Expand Down
27 changes: 15 additions & 12 deletions superset/assets/javascripts/explorev2/components/Filter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,24 +61,27 @@ export default class Filter extends React.Component {
if (!filter.choices) {
this.fetchFilterValues(filter.col);
}
}
if (this.props.opChoices.indexOf('==') !== -1) {
// druid having filter
return (
<SelectField
multi
freeForm
name="filter-value"
<input
type="text"
onChange={this.changeFilter.bind(this, 'val')}
value={filter.value}
choices={filter.choices}
onChange={this.changeFilter.bind(this, 'value')}
className="form-control input-sm"
placeholder="Filter value"
/>
);
}
return (
<input
type="text"
onChange={this.changeFilter.bind(this, 'value')}
value={filter.value}
className="form-control input-sm"
placeholder="Filter value"
<SelectField
multi
freeForm
name="filter-value"
value={filter.val}
choices={filter.choices || []}
onChange={this.changeFilter.bind(this, 'val')}
/>
);
}
Expand Down
40 changes: 15 additions & 25 deletions superset/assets/javascripts/explorev2/components/FilterField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,26 @@ import { Button, Row, Col } from 'react-bootstrap';
import Filter from './Filter';

const propTypes = {
prefix: PropTypes.string,
choices: PropTypes.array,
onChange: PropTypes.func,
value: PropTypes.array,
datasource: PropTypes.object,
opChoices: PropTypes.array.isRequired,
};

const defaultProps = {
prefix: 'flt',
choices: [],
onChange: () => {},
value: [],
};

export default class FilterField extends React.Component {
constructor(props) {
super(props);
this.opChoices = props.prefix === 'flt' ?
['in', 'not in'] : ['==', '!=', '>', '<', '>=', '<='];
}
addFilter() {
const newFilters = Object.assign([], this.props.value);
newFilters.push({
prefix: this.props.prefix,
col: null,
op: 'in',
value: this.props.datasource.filter_select ? [] : '',
val: this.props.datasource.filter_select ? [] : '',
});
this.props.onChange(newFilters);
}
Expand All @@ -46,22 +39,19 @@ export default class FilterField extends React.Component {
render() {
const filters = [];
this.props.value.forEach((filter, i) => {
// only display filters with current prefix
if (filter.prefix === this.props.prefix) {
const filterBox = (
<div key={i}>
<Filter
filter={filter}
choices={this.props.choices}
opChoices={this.opChoices}
datasource={this.props.datasource}
removeFilter={this.removeFilter.bind(this, i)}
changeFilter={this.changeFilter.bind(this, i)}
/>
</div>
);
filters.push(filterBox);
}
const filterBox = (
<div key={i}>
<Filter
filter={filter}
choices={this.props.choices}
opChoices={this.props.opChoices}
datasource={this.props.datasource}
removeFilter={this.removeFilter.bind(this, i)}
changeFilter={this.changeFilter.bind(this, i)}
/>
</div>
);
filters.push(filterBox);
});
return (
<div>
Expand Down
47 changes: 1 addition & 46 deletions superset/assets/javascripts/explorev2/exploreUtils.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,16 @@
/* eslint camelcase: 0 */
function formatFilters(filters) {
// outputs an object of url params of filters
// prefix can be 'flt' or 'having'
const params = {};
for (let i = 0; i < filters.length; i++) {
const filter = filters[i];
params[`${filter.prefix}_col_${i + 1}`] = filter.col;
params[`${filter.prefix}_op_${i + 1}`] = filter.op;
if (filter.value.constructor === Array) {
params[`${filter.prefix}_eq_${i + 1}`] = filter.value.join(',');
} else {
params[`${filter.prefix}_eq_${i + 1}`] = filter.value;
}
}
return params;
}

export function parseFilters(form_data, prefix = 'flt') {
const filters = [];
for (let i = 0; i <= 10; i++) {
if (form_data[`${prefix}_col_${i}`] && form_data[`${prefix}_op_${i}`]) {
filters.push({
prefix,
col: form_data[`${prefix}_col_${i}`],
op: form_data[`${prefix}_op_${i}`],
value: form_data[`${prefix}_eq_${i}`],
});
}
/* eslint no-param-reassign: 0 */
delete form_data[`${prefix}_col_${i}`];
delete form_data[`${prefix}_op_${i}`];
delete form_data[`${prefix}_eq_${i}`];
}
return filters;
}

export function getFilters(form_data, datasource_type) {
if (datasource_type === 'table') {
return parseFilters(form_data);
}
return parseFilters(form_data).concat(parseFilters(form_data, 'having'));
}

export function getParamObject(form_data, datasource_type, saveNewSlice) {
const data = {
datasource_id: form_data.datasource,
datasource_type,
};
Object.keys(form_data).forEach((field) => {
// filter out null fields
if (form_data[field] !== null && field !== 'datasource' && field !== 'filters'
if (form_data[field] !== null && field !== 'datasource'
&& !(saveNewSlice && field === 'slice_name')) {
data[field] = form_data[field];
}
});
const filterParams = formatFilters(form_data.filters);
Object.assign(data, filterParams);
return data;
}

Expand Down
3 changes: 0 additions & 3 deletions superset/assets/javascripts/explorev2/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import thunk from 'redux-thunk';
import { now } from '../modules/dates';
import { initEnhancer } from '../reduxUtils';
import { getFieldsState } from './stores/store';
import { getFilters } from './exploreUtils';


// jquery and bootstrap required to make bootstrap dropdown menu's work
Expand All @@ -23,7 +22,6 @@ const bootstrapData = JSON.parse(exploreViewContainer.getAttribute('data-bootstr
import { exploreReducer } from './reducers/exploreReducer';
const formData = bootstrapData.form_data;
const fields = getFieldsState(bootstrapData, formData);
fields.filters.value = getFilters(formData, bootstrapData.datasource_type);
delete bootstrapData.form_data;

// Initial state
Expand All @@ -41,7 +39,6 @@ const bootstrappedState = Object.assign(
}
);


const store = createStore(exploreReducer, bootstrappedState,
compose(applyMiddleware(thunk), initEnhancer(false))
);
Expand Down
14 changes: 14 additions & 0 deletions superset/assets/javascripts/explorev2/stores/fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,20 @@ export const fields = {
mapStateToProps: (state) => ({
choices: (state.datasource) ? state.datasource.filterable_cols : [],
datasource: state.datasource,
opChoices: ['in', 'not in'],
}),
},

having_filters: {
type: 'FilterField',
label: '',
default: [],
description: '',
mapStateToProps: (state) => ({
choices: (state.datasource) ? state.datasource.metrics_combo
.concat(state.datasource.filterable_cols) : [],
datasource: state.datasource,
opChoices: ['==', '!=', '>', '<', '>=', '<='],
}),
},
};
Expand Down
5 changes: 2 additions & 3 deletions superset/assets/javascripts/explorev2/stores/visTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,13 @@ export const commonControlPanelSections = {
'Leave the value field empty to filter empty strings or nulls' +
'For filters with comma in values, wrap them in single quotes' +
"as in <NY, 'Tahoe, CA', DC>",
prefix: 'flt',
fieldSetRows: [['filters']],
},
{
label: 'Result Filters',
description: 'The filters to apply after post-aggregation.' +
'Leave the value field empty to filter empty strings or nulls',
prefix: 'having',
fieldSetRows: [['filters']],
fieldSetRows: [['having_filters']],
},
],
};
Expand Down Expand Up @@ -433,6 +431,7 @@ const visTypes = {
},

big_number_total: {
label: 'Big Number',
controlPanelSections: [
{
label: null,
Expand Down
1 change: 0 additions & 1 deletion superset/assets/version_info.json

This file was deleted.

47 changes: 47 additions & 0 deletions superset/migrations/versions/53fc3de270ae_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""update filters for slice model
Revision ID: 53fc3de270ae
Revises: db0c65b146bd
Create Date: 2017-01-31 10:18:49.071296
"""

# revision identifiers, used by Alembic.
revision = '53fc3de270ae'
down_revision = 'db0c65b146bd'

from alembic import op
from superset import db, cast_filter_data
import json
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text

Base = declarative_base()


class Slice(Base):
"""Declarative class to do query in upgrade"""
__tablename__ = 'slices'
id = Column(Integer, primary_key=True)
datasource_id = Column(Integer)
druid_datasource_id = Column(Integer)
table_id = Column(Integer)
datasource_type = Column(String(200))
params = Column(Text)


def upgrade():
bind = op.get_bind()
session = db.Session(bind=bind)

for slc in session.query(Slice).all():
d = json.loads(slc.params)
d = cast_filter_data(d)
slc.params = json.dumps(d)
session.merge(slc)
session.commit()
session.close()


def downgrade():
pass
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""update_slice_model_json
Revision ID: db0c65b146bd
Revises: 1296d28ec131
Revises: f18570e03440
Create Date: 2017-01-24 12:31:06.541746
"""

# revision identifiers, used by Alembic.
revision = 'db0c65b146bd'
down_revision = '1296d28ec131'
down_revision = 'f18570e03440'

from alembic import op
from superset import db, cast_form_data
Expand Down
4 changes: 1 addition & 3 deletions superset/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2505,9 +2505,7 @@ def get_filters(raw_filters):
cond = ~(Dimension(col) == eq)
elif op in ('in', 'not in'):
fields = []
# Distinguish quoted values with regular value types
splitted = FillterPattern.split(eq)[1::2]
values = [types.replace("'", '') for types in splitted]
values = eq
if len(values) > 1:
for s in values:
s = s.strip()
Expand Down
Loading

0 comments on commit 9fdc543

Please sign in to comment.