diff --git a/caravel/assets/javascripts/explorev2/actions/exploreActions.js b/caravel/assets/javascripts/explorev2/actions/exploreActions.js index d836dd864bb44..194d292b151a8 100644 --- a/caravel/assets/javascripts/explorev2/actions/exploreActions.js +++ b/caravel/assets/javascripts/explorev2/actions/exploreActions.js @@ -1,11 +1,6 @@ const $ = window.$ = require('jquery'); export const SET_DATASOURCE = 'SET_DATASOURCE'; -export const SET_TIME_COLUMN_OPTS = 'SET_TIME_COLUMN_OPTS'; -export const SET_TIME_GRAIN_OPTS = 'SET_TIME_GRAIN_OPTS'; -export const SET_GROUPBY_COLUMN_OPTS = 'SET_GROUPBY_COLUMN_OPTS'; -export const SET_METRICS_OPTS = 'SET_METRICS_OPTS'; -export const SET_COLUMN_OPTS = 'SET_COLUMN_OPTS'; -export const SET_ORDERING_OPTS = 'SET_ORDERING_OPTS'; +export const SET_FIELD_OPTIONS = 'SET_FIELD_OPTIONS'; export const TOGGLE_SEARCHBOX = 'TOGGLE_SEARCHBOX'; export const SET_FILTER_COLUMN_OPTS = 'SET_FILTER_COLUMN_OPTS'; export const ADD_FILTER = 'ADD_FILTER'; @@ -19,47 +14,36 @@ export const CLEAR_ALL_OPTS = 'CLEAR_ALL_OPTS'; export const SET_DATASOURCE_TYPE = 'SET_DATASOURCE_TYPE'; export const SET_FIELD_VALUE = 'SET_FIELD_VALUE'; -export function setTimeColumnOpts(timeColumnOpts) { - return { type: SET_TIME_COLUMN_OPTS, timeColumnOpts }; +export function setFieldOptions(options) { + return { type: SET_FIELD_OPTIONS, options }; } -export function setTimeGrainOpts(timeGrainOpts) { - return { type: SET_TIME_GRAIN_OPTS, timeGrainOpts }; -} - -export function setGroupByColumnOpts(groupByColumnOpts) { - return { type: SET_GROUPBY_COLUMN_OPTS, groupByColumnOpts }; -} - -export function setMetricsOpts(metricsOpts) { - return { type: SET_METRICS_OPTS, metricsOpts }; +export function clearAllOpts() { + return { type: CLEAR_ALL_OPTS }; } -export function setColumnOpts(columnOpts) { - return { type: SET_COLUMN_OPTS, columnOpts }; +export function setDatasourceType(datasourceType) { + return { type: SET_DATASOURCE_TYPE, datasourceType }; } -export function setOrderingOpts(orderingOpts) { - return { type: SET_ORDERING_OPTS, orderingOpts }; +export const FETCH_STARTED = 'FETCH_STARTED'; +export function fetchStarted() { + return { type: FETCH_STARTED }; } -export function setFilterColumnOpts(filterColumnOpts) { - return { type: SET_FILTER_COLUMN_OPTS, filterColumnOpts }; +export const FETCH_SUCCEEDED = 'FETCH_SUCCEEDED'; +export function fetchSucceeded() { + return { type: FETCH_SUCCEEDED }; } -export function clearAllOpts() { - return { type: CLEAR_ALL_OPTS }; +export const FETCH_FAILED = 'FETCH_FAILED'; +export function fetchFailed() { + return { type: FETCH_FAILED }; } -export function setFormOpts(datasourceId, datasourceType) { +export function fetchFieldOptions(datasourceId, datasourceType) { return function (dispatch) { - const timeColumnOpts = []; - const groupByColumnOpts = []; - const metricsOpts = []; - const filterColumnOpts = []; - const timeGrainOpts = []; - const columnOpts = []; - const orderingOpts = []; + dispatch(fetchStarted()); if (datasourceId) { const params = [`datasource_id=${datasourceId}`, `datasource_type=${datasourceType}`]; @@ -67,41 +51,15 @@ export function setFormOpts(datasourceId, datasourceType) { $.get(url, (data, status) => { if (status === 'success') { - data.time_columns.forEach((d) => { - if (d) timeColumnOpts.push({ value: d, label: d }); - }); - data.groupby_cols.forEach((d) => { - if (d) groupByColumnOpts.push({ value: d, label: d }); - }); - data.metrics.forEach((d) => { - if (d) metricsOpts.push({ value: d[1], label: d[0] }); - }); - data.filter_cols.forEach((d) => { - if (d) filterColumnOpts.push({ value: d, label: d }); - }); - data.time_grains.forEach((d) => { - if (d) timeGrainOpts.push({ value: d, label: d }); - }); - data.columns.forEach((d) => { - if (d) columnOpts.push({ value: d, label: d }); - }); - data.ordering_cols.forEach((d) => { - if (d) orderingOpts.push({ value: d, label: d }); - }); - - // Repopulate options for controls - dispatch(setTimeColumnOpts(timeColumnOpts)); - dispatch(setTimeGrainOpts(timeGrainOpts)); - dispatch(setGroupByColumnOpts(groupByColumnOpts)); - dispatch(setMetricsOpts(metricsOpts)); - dispatch(setFilterColumnOpts(filterColumnOpts)); - dispatch(setColumnOpts(columnOpts)); - dispatch(setOrderingOpts(orderingOpts)); + // populate options for select type fields + dispatch(setFieldOptions(data.field_options)); + dispatch(fetchSucceeded()); + } else if (status === 'error') { + dispatch(fetchFailed()); } }); } else { - // Clear all Select options - dispatch(clearAllOpts()); + // in what case don't we have a datasource id? } }; } diff --git a/caravel/assets/javascripts/explorev2/components/ControlPanelsContainer.jsx b/caravel/assets/javascripts/explorev2/components/ControlPanelsContainer.jsx index 7b2a8de9dc5a4..dea200632db81 100644 --- a/caravel/assets/javascripts/explorev2/components/ControlPanelsContainer.jsx +++ b/caravel/assets/javascripts/explorev2/components/ControlPanelsContainer.jsx @@ -13,6 +13,8 @@ const propTypes = { datasource_id: PropTypes.number.isRequired, datasource_type: PropTypes.string.isRequired, actions: PropTypes.object.isRequired, + fields: PropTypes.object.isRequired, + isDatasourceMetaLoading: PropTypes.bool.isRequired, }; const defaultProps = { @@ -23,7 +25,7 @@ class ControlPanelsContainer extends React.Component { componentWillMount() { const { datasource_id, datasource_type } = this.props; if (datasource_id) { - this.props.actions.setFormOpts(datasource_id, datasource_type); + this.props.actions.fetchFieldOptions(datasource_id, datasource_type); } } @@ -47,27 +49,30 @@ class ControlPanelsContainer extends React.Component { render() { return ( -
-
- {this.sectionsToRender().map((section) => ( - - {section.fieldSetRows.map((fieldSets, i) => ( - - ))} - - ))} - {/* TODO: add filters section */} + {!this.props.isDatasourceMetaLoading && +
+
+ {this.sectionsToRender().map((section) => ( + + {section.fieldSetRows.map((fieldSets, i) => ( + + ))} + + ))} + {/* TODO: add filters section */} +
-
+ } ); } @@ -78,6 +83,8 @@ ControlPanelsContainer.defaultProps = defaultProps; function mapStateToProps(state) { return { + isDatasourceMetaLoading: state.isDatasourceMetaLoading, + fields: state.fields, datasource_id: state.datasource_id, datasource_type: state.datasource_type, viz_type: state.viz.form_data.viz_type, diff --git a/caravel/assets/javascripts/explorev2/components/FieldSetRow.jsx b/caravel/assets/javascripts/explorev2/components/FieldSetRow.jsx index 22d7b56848e1c..9cc5c2c946717 100644 --- a/caravel/assets/javascripts/explorev2/components/FieldSetRow.jsx +++ b/caravel/assets/javascripts/explorev2/components/FieldSetRow.jsx @@ -1,10 +1,10 @@ import React, { PropTypes } from 'react'; import FieldSet from './FieldSet'; -import { fields } from '../stores/store'; const NUM_COLUMNS = 12; const propTypes = { + fields: PropTypes.object.isRequired, fieldSets: PropTypes.array.isRequired, fieldOverrides: PropTypes.object, onChange: PropTypes.func, @@ -15,29 +15,33 @@ const defaultProps = { onChange: () => {}, }; -function getFieldData(fs, fieldOverrides) { - let fieldData = fields[fs]; - if (fieldOverrides.hasOwnProperty(fs)) { - const overrideData = fieldOverrides[fs]; - fieldData = Object.assign({}, fieldData, overrideData); +export default class FieldSetRow extends React.Component { + getFieldData(fs) { + const { fields, fieldOverrides } = this.props; + let fieldData = fields[fs]; + if (fieldOverrides.hasOwnProperty(fs)) { + const overrideData = fieldOverrides[fs]; + fieldData = Object.assign({}, fieldData, overrideData); + } + return fieldData; } - return fieldData; -} -export default function FieldSetRow({ fieldSets, fieldOverrides, onChange }) { - const colSize = NUM_COLUMNS / fieldSets.length; - return ( -
- {fieldSets.map((fs) => { - const fieldData = getFieldData(fs, fieldOverrides); - return ( -
-
-
- ); - })} -
- ); + render() { + const colSize = NUM_COLUMNS / this.props.fieldSets.length; + const { onChange } = this.props; + return ( +
+ {this.props.fieldSets.map((fs) => { + const fieldData = this.getFieldData(fs); + return ( +
+
+
+ ); + })} +
+ ); + } } FieldSetRow.propTypes = propTypes; diff --git a/caravel/assets/javascripts/explorev2/components/SelectField.jsx b/caravel/assets/javascripts/explorev2/components/SelectField.jsx index c50e0cdf59fad..bfd0b6a187978 100644 --- a/caravel/assets/javascripts/explorev2/components/SelectField.jsx +++ b/caravel/assets/javascripts/explorev2/components/SelectField.jsx @@ -5,6 +5,7 @@ import { slugify } from '../../modules/utils'; const propTypes = { name: PropTypes.string.isRequired, + choices: PropTypes.array, label: PropTypes.string, description: PropTypes.string, onChange: PropTypes.func, @@ -20,6 +21,7 @@ export default class SelectField extends React.Component { onChange(opt) { this.props.onChange(this.props.name, opt.target.value); } + render() { return ( @@ -32,8 +34,7 @@ export default class SelectField extends React.Component { placeholder="select" onChange={this.onChange.bind(this)} > - - + {this.props.choices.map((c) => )} ); diff --git a/caravel/assets/javascripts/explorev2/reducers/exploreReducer.js b/caravel/assets/javascripts/explorev2/reducers/exploreReducer.js index 93d7fe5f09bb6..7e918111b6cc9 100644 --- a/caravel/assets/javascripts/explorev2/reducers/exploreReducer.js +++ b/caravel/assets/javascripts/explorev2/reducers/exploreReducer.js @@ -4,24 +4,39 @@ import { addToArr, removeFromArr, alterInArr } from '../../../utils/reducerUtils export const exploreReducer = function (state, action) { const actionHandlers = { - [actions.SET_TIME_COLUMN_OPTS]() { - return Object.assign({}, state, { timeColumnOpts: action.timeColumnOpts }); + [actions.SET_DATASOURCE]() { + return Object.assign({}, state, { datasourceId: action.datasourceId }); }, - [actions.SET_TIME_GRAIN_OPTS]() { - return Object.assign({}, state, { timeGrainOpts: action.timeGrainOpts }); + + [actions.FETCH_STARTED]() { + return Object.assign({}, state, { isDatasourceMetaLoading: true }); }, - [actions.SET_GROUPBY_COLUMN_OPTS]() { - return Object.assign({}, state, { groupByColumnOpts: action.groupByColumnOpts }); + + [actions.FETCH_SUCCEEDED]() { + return Object.assign({}, state, { isDatasourceMetaLoading: false }); }, - [actions.SET_METRICS_OPTS]() { - return Object.assign({}, state, { metricsOpts: action.metricsOpts }); + + [actions.FETCH_FAILED]() { + // todo(alanna) handle failure/error state + return Object.assign({}, state, { isDatasourceMetaLoading: false }); }, - [actions.SET_COLUMN_OPTS]() { - return Object.assign({}, state, { columnOpts: action.columnOpts }); + + [actions.SET_FIELD_OPTIONS]() { + const newState = Object.assign({}, state); + const optionsByFieldName = action.options; + const fieldNames = Object.keys(optionsByFieldName); + + fieldNames.forEach((fieldName) => { + newState.fields[fieldName].choices = optionsByFieldName[fieldName]; + }); + + return Object.assign({}, state, newState); }, - [actions.SET_ORDERING_OPTS]() { - return Object.assign({}, state, { orderingOpts: action.orderingOpts }); + + [actions.TOGGLE_SEARCHBOX]() { + return Object.assign({}, state, { searchBox: action.searchBox }); }, + [actions.SET_FILTER_COLUMN_OPTS]() { return Object.assign({}, state, { filterColumnOpts: action.filterColumnOpts }); }, diff --git a/caravel/assets/javascripts/explorev2/stores/store.js b/caravel/assets/javascripts/explorev2/stores/store.js index d5db2f9c17fe9..57b1d6dc2e9d1 100644 --- a/caravel/assets/javascripts/explorev2/stores/store.js +++ b/caravel/assets/javascripts/explorev2/stores/store.js @@ -10,7 +10,6 @@ export const fieldTypes = [ 'TextAreaFeild', 'TextField', ]; - const D3_FORMAT_DOCS = 'D3 format syntax: https://github.com/d3/d3-format'; // input choices & options @@ -708,13 +707,12 @@ export const visTypes = { }, }; -// todo: complete the choices and default keys from forms.py export const fields = { datasource: { type: 'SelectField', label: 'Datasource', - default: '', - choices: [['datasource', 'datasource']], + default: null, + choices: [], description: '', }, @@ -729,23 +727,23 @@ export const fields = { metrics: { type: 'SelectMultipleSortableField', label: 'Metrics', - choices: [[1, 1]], - default: ['todo'], + choices: [], + default: null, description: 'One or many metrics to display', }, order_by_cols: { type: 'SelectMultipleSortableField', label: 'Ordering', - choices: 'todo: order_by_choices', + choices: [], description: 'One or many metrics to display', }, metric: { type: 'SelectField', label: 'Metric', - choices: 'todo: ', - default: 'todo: ', + choices: [], + default: null, description: 'Choose the metric', }, @@ -825,7 +823,7 @@ export const fields = { type: 'SelectField', label: 'YScale Interval', choices: formatSelectOptionsForRange(1, 50), - default: '1', + default: null, description: 'Number of steps to take between ticks when ' + 'displaying the Y scale', }, @@ -834,7 +832,7 @@ export const fields = { type: 'CheckboxField', label: 'Stacked Bars', default: false, - description: '', + description: null, }, show_markers: { @@ -882,7 +880,7 @@ export const fields = { type: 'SelectField', label: 'Color Metric', choices: [], - default: '', + default: null, description: 'A metric to use for color', }, @@ -910,30 +908,30 @@ export const fields = { columns: { type: 'SelectMultipleSortableField', label: 'Columns', - choices: [[1, 1]], + choices: [], description: 'One or many fields to pivot as columns', }, all_columns: { type: 'SelectMultipleSortableField', label: 'Columns', - choices: [['all_columns', 'all_columns']], + choices: [], description: 'Columns to display', }, all_columns_x: { type: 'SelectField', label: 'X', - choices: [['all_columns_x', 'all_columns_x']], - default: '', + choices: [], + default: null, description: 'Columns to display', }, all_columns_y: { type: 'SelectField', label: 'Y', - choices: [['all_columns_y', 'all_columns_y']], - default: '', + choices: [], + default: null, description: 'Columns to display', }, @@ -944,7 +942,7 @@ export const fields = { ['', 'default'], ['now', 'now'], ], - default: '', + default: null, description: 'Defines the origin where time buckets start, ' + 'accepts natural dates as in `now`, `sunday` or `1970-01-01`', }, @@ -971,6 +969,10 @@ export const fields = { '6 hour', '1 day', '7 days', + 'week', + 'week_starting_sunday', + 'week_ending_saturday', + 'month', ]), description: 'The time granularity for the visualization. Note that you ' + 'can type and use simple natural language as in `10 seconds`, ' + @@ -1024,8 +1026,8 @@ export const fields = { granularity_sqla: { type: 'SelectField', label: 'Time Column', - default: 'granularity_sqla', - choices: [['granularity_sqla', 'granularity_sqla']], + default: null, + choices: [], description: '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 ' + @@ -1035,7 +1037,7 @@ export const fields = { time_grain: { label: 'Time Grain', - choices: ['grains-choices'], + choices: [], default: 'Time Column', description: 'The time granularity for the visualization. This ' + 'applies a date transformation to alter ' + @@ -1047,7 +1049,7 @@ export const fields = { resample_rule: { type: 'FreeFormSelectField', label: 'Resample Rule', - default: '', + default: null, choices: formatSelectOptions(['', '1T', '1H', '1D', '7D', '1M', '1AS']), description: 'Pandas resample rule', }, @@ -1055,7 +1057,7 @@ export const fields = { resample_how: { type: 'SelectField', label: 'Resample How', - default: '', + default: null, choices: formatSelectOptions(['', 'mean', 'sum', 'median']), description: 'Pandas resample how', }, @@ -1063,7 +1065,7 @@ export const fields = { resample_fillmethod: { type: 'SelectField', label: 'Resample Fill Method', - default: '', + default: null, choices: formatSelectOptions(['', 'ffill', 'bfill']), description: 'Pandas resample fill method', }, @@ -1129,7 +1131,7 @@ export const fields = { number_format: { type: 'SelectField', label: 'Number format', - default: '.3s', + default: D3_TIME_FORMAT_OPTIONS[0], choices: D3_TIME_FORMAT_OPTIONS, description: D3_FORMAT_DOCS, }, @@ -1137,7 +1139,7 @@ export const fields = { row_limit: { type: 'SelectField', label: 'Row limit', - default: '', + default: null, choices: formatSelectOptions(ROW_LIMIT_OPTIONS), }, @@ -1152,8 +1154,8 @@ export const fields = { timeseries_limit_metric: { type: 'SelectField', label: 'Sort By', - choices: [['', ''], ['timeseries_limit_metric', 'timeseries_limit_metric']], - default: '', + choices: [], + default: null, description: 'Metric used to define the top series', }, @@ -1169,7 +1171,7 @@ export const fields = { rolling_periods: { type: 'IntegerField', label: 'Periods', - validators: ['todo: [validators.optional()]'], + validators: [], description: 'Defines the size of the rolling window function, ' + 'relative to the time granularity selected', }, @@ -1177,8 +1179,8 @@ export const fields = { series: { type: 'SelectField', label: 'Series', - choices: formatSelectOptions(['', 'series']), - default: '', + choices: [], + default: null, description: 'Defines the grouping of entities. ' + 'Each series is shown as a specific color on the chart and ' + 'has a legend toggle', @@ -1187,32 +1189,32 @@ export const fields = { entity: { type: 'SelectField', label: 'Entity', - choices: formatSelectOptions(['', 'entity']), - default: '', + choices: [], + default: null, description: 'This define the element to be plotted on the chart', }, x: { type: 'SelectField', label: 'X Axis', - choices: formatSelectOptions(['', 'metrics assigned to x']), - default: '', + choices: [], + default: null, description: 'Metric assigned to the [X] axis', }, y: { type: 'SelectField', label: 'Y Axis', - choices: formatSelectOptions(['', 'metrics assigned to x']), - default: '', + choices: [], + default: null, description: 'Metric assigned to the [Y] axis', }, size: { type: 'SelectField', label: 'Bubble Size', - default: '', - choices: formatSelectOptions(['', 'bubble-size']), + default: null, + choices: [], }, url: { @@ -1271,7 +1273,7 @@ export const fields = { type: 'SelectField', label: 'Table Timestamp Format', default: 'smart_date', - choices: formatSelectOptions(TIME_STAMP_OPTIONS), + choices: TIME_STAMP_OPTIONS, description: 'Timestamp Format', }, @@ -1287,7 +1289,7 @@ export const fields = { type: 'SelectCustomMultiField', label: 'X axis format', default: 'smart_date', - choices: formatSelectOptions(TIME_STAMP_OPTIONS), + choices: TIME_STAMP_OPTIONS, description: D3_FORMAT_DOCS, }, @@ -1477,7 +1479,7 @@ export const fields = { type: 'IntegerField', label: 'Period Ratio', default: '', - validators: 'todo: [validators.optional()]', + validators: [], description: '[integer] Number of period to compare against, ' + 'this is relative to the granularity selected', }, @@ -1494,7 +1496,7 @@ export const fields = { time_compare: { type: 'TextField', label: 'Time Shift', - default: '', + default: null, description: 'Overlay a timeseries from a ' + 'relative time period. Expects relative time delta ' + 'in natural language (example: 24 hours, 7 days, ' + @@ -1510,7 +1512,7 @@ export const fields = { mapbox_label: { type: 'SelectMultipleSortableField', label: 'label', - choices: "todo: formatSelectOptions(['count'] + datasource.column_names)", + choices: [], description: '`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. ' + @@ -1555,8 +1557,8 @@ export const fields = { point_radius: { type: 'SelectField', label: 'Point Radius', - default: 'Auto', - choices: "todo: formatSelectOptions(['Auto'] + datasource.column_names)", + default: null, + choices: [], description: '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', @@ -1582,7 +1584,7 @@ export const fields = { type: 'IntegerField', label: 'Zoom', default: 11, - validators: 'todo: [validators.optional()]', + validators: [], description: 'Zoom level of the map', places: 8, }, @@ -1646,25 +1648,10 @@ export const defaultViz = { }; export const initialState = { + isDatasourceMetaLoading: false, datasources: null, datasource_id: null, datasource_type: null, - timeColumnOpts: [], - timeGrainOpts: [], - groupByColumnOpts: [], - metricsOpts: [], - columnOpts: [], - orderingOpts: [], - filterColumnOpts: [], + fields, viz: defaultViz, }; - -export const defaultOpts = { - timeColumnOpts: [], - timeGrainOpts: [], - groupByColumnOpts: [], - metricsOpts: [], - filterColumnOpts: [], - columnOpts: [], - orderingOpts: [], -}; diff --git a/caravel/assets/spec/javascripts/explorev2/components/ControlPanelsContainer_spec.js b/caravel/assets/spec/javascripts/explorev2/components/ControlPanelsContainer_spec.js index 924fb42b468b8..5c56381b90eeb 100644 --- a/caravel/assets/spec/javascripts/explorev2/components/ControlPanelsContainer_spec.js +++ b/caravel/assets/spec/javascripts/explorev2/components/ControlPanelsContainer_spec.js @@ -13,7 +13,7 @@ const defaultProps = { datasource_id: 1, datasource_type: 'type', actions: { - setFormOpts: () => { + fetchFieldOptions: () => { // noop }, }, diff --git a/caravel/assets/spec/javascripts/explorev2/components/SelectField_spec.js b/caravel/assets/spec/javascripts/explorev2/components/SelectField_spec.js index 7b22cf010f72b..29af23f1bf432 100644 --- a/caravel/assets/spec/javascripts/explorev2/components/SelectField_spec.js +++ b/caravel/assets/spec/javascripts/explorev2/components/SelectField_spec.js @@ -8,6 +8,7 @@ import { shallow } from 'enzyme'; import SelectField from '../../../../javascripts/explorev2/components/SelectField'; const defaultProps = { + choices: [[10, 10], [20, 20]], name: 'row_limit', label: 'Row Limit', onChange: sinon.spy(), diff --git a/caravel/views.py b/caravel/views.py index 28e81041e6e60..d3c9320479b4a 100755 --- a/caravel/views.py +++ b/caravel/views.py @@ -2106,6 +2106,9 @@ def fetch_datasource_metadata(self): .first() ) + datasources = db.session.query(datasource_class).all() + datasources = sorted(datasources, key=lambda ds: ds.full_name) + # Check if datasource exists if not datasource: return json_error_response(DATASOURCE_MISSING_ERR) @@ -2113,23 +2116,38 @@ def fetch_datasource_metadata(self): if not self.datasource_access(datasource): return json_error_response(DATASOURCE_ACCESS_ERR) + gb_cols = [(col, col) for col in datasource.groupby_column_names] order_by_choices = [] for s in sorted(datasource.num_cols): - order_by_choices.append(s + ' [asc]') - order_by_choices.append(s + ' [desc]') - column_opts = { - "groupby_cols": datasource.groupby_column_names, - "metrics": datasource.metrics_combo, - "filter_cols": datasource.filterable_column_names, - "columns": datasource.column_names, - "ordering_cols": order_by_choices + order_by_choices.append((json.dumps([s, True]), s + ' [asc]')) + order_by_choices.append((json.dumps([s, False]), s + ' [desc]')) + + field_options = { + 'datasource': [(d.id, d.full_name) for d in datasources], + 'metrics': datasource.metrics_combo, + 'order_by_cols': order_by_choices, + 'metric': datasource.metrics_combo, + 'secondary_metric': datasource.metrics_combo, + 'groupby': gb_cols, + 'columns': gb_cols, + 'all_columns': datasource.column_names, + 'all_columns_x': datasource.column_names, + 'all_columns_y': datasource.column_names, + 'granularity_sqla': datasource.dttm_cols, + 'timeseries_limit_metric': [('', '')] + datasource.metrics_combo, + 'series': gb_cols, + 'entity': gb_cols, + 'x': datasource.metrics_combo, + 'y': datasource.metrics_combo, + 'size': datasource.metrics_combo, + 'mapbox_label': datasource.column_names, + 'point_radius': ["Auto"] + datasource.column_names, } - form_data = dict( - column_opts.items() + datasource.time_column_grains.items() - ) return Response( - json.dumps(form_data), mimetype="application/json") + json.dumps({'field_options': field_options}), + mimetype="application/json" + ) @has_access @expose("/queries/") diff --git a/caravel/viz.py b/caravel/viz.py index 7b34daa5cacf3..5633ebe7e8dcd 100755 --- a/caravel/viz.py +++ b/caravel/viz.py @@ -317,6 +317,7 @@ def get_json(self, force=False): if not payload: is_cached = False cache_timeout = self.cache_timeout + payload = { 'cache_timeout': cache_timeout, 'cache_key': cache_key, diff --git a/tests/core_tests.py b/tests/core_tests.py index a57f244ac0373..b4b709738365d 100644 --- a/tests/core_tests.py +++ b/tests/core_tests.py @@ -387,6 +387,11 @@ def test_table_metadata(self): elif backend == 'postgresql': self.assertEqual(len(data.get('indexes')), 5) + def test_fetch_datasource_metadata(self): + self.login(username='admin') + url = '/caravel/fetch_datasource_metadata?datasource_type=table&datasource_id=1'; + resp = json.loads(self.get_resp(url)) + self.assertEqual(len(resp['field_options']), 19) if __name__ == '__main__':