Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 114 additions & 81 deletions caravel/assets/javascripts/explore/explore.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,12 @@ require('../../vendor/pygments.css');
require('../../stylesheets/explore.css');

let slice;
let filterCount = 1;

const getPanelClass = function (fieldPrefix) {
return (fieldPrefix === 'flt' ? 'filter' : 'having') + '_panel';
};

function prepForm() {
// Assigning the right id to form elements in filters
const fixId = function ($filter, fieldPrefix, i) {
$filter.attr('id', function () {
return fieldPrefix + '_' + i;
});

['col', 'op', 'eq'].forEach(function (fieldMiddle) {
const fieldName = fieldPrefix + '_' + fieldMiddle;
$filter.find('[id^=' + fieldName + '_]')
.attr('id', function () {
return fieldName + '_' + i;
})
.attr('name', function () {
return fieldName + '_' + i;
});
});
};

['flt', 'having'].forEach(function (fieldPrefix) {
let i = 1;
$('#' + getPanelClass(fieldPrefix) + ' #filters > div').each(function () {
fixId($(this), fieldPrefix, i);
i++;
});
});
}

function query(forceUpdate, pushState) {
let force = forceUpdate;
if (force === undefined) {
Expand All @@ -67,10 +40,8 @@ function query(forceUpdate, pushState) {
$('div.alert').remove();
}
$('#is_cached').hide();
prepForm();

if (pushState !== false) {
// update the url after prepForm() fix the field ids
history.pushState({}, document.title, slice.querystring());
}
slice.render(force);
Expand Down Expand Up @@ -104,7 +75,6 @@ function saveSlice() {
return;
}
$('#action').val(action);
prepForm();
$('#query').submit();
}

Expand Down Expand Up @@ -199,22 +169,128 @@ function initExploreView() {
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
$('.ui-helper-hidden-accessible').remove(); // jQuery-ui 1.11+ creates a div for every tooltip

function createChoices(term, data) {
const filtered = $(data).filter(function () {
return this.text.localeCompare(term) === 0;
});
if (filtered.length === 0) {
return {
id: term,
text: term,
};
}
return {};
}

function convertSelect(selectId, multiple) {
const parent = $(selectId).parent();
const name = $(selectId).attr('name');
const l = [];
let selected = '';
const options = $(selectId + ' option');
const classes = $(selectId).attr('class');
for (let i = 0; i < options.length; i++) {
l.push({
id: options[i].value,
text: options[i].text,
});
if (options[i].selected) {
selected = options[i].value;
}
}
$(selectId).remove();
parent.append(
'<input class="' + classes + '" id="' + selectId.substring(1) +
'" name="' + name + '" type="text" value="' + selected + '">'
);
$(`input[name='${name}']`).select2({
createSearchChoice: createChoices,
dropdownAutoWidth: true,
multiple,
data: l,
sortResults(data) {
return data.sort(function (a, b) {
if (a.text > b.text) {
return 1;
} else if (a.text < b.text) {
return -1;
}
return 0;
});
},
});
}

function insertFilterChoices(i, fieldPrefix) {
const column = $('#' + fieldPrefix + '_col_' + i).val();
const eq = '#' + fieldPrefix + '_eq_' + i;
$(eq).empty();

$.getJSON(slice.filterEndpoint(column), function (data) {
$(eq).append($('<option></option>').attr('value', null).text(''));
$.each(data, function (key, value) {
let wrappedValue = value;
if (value.indexOf(',') !== -1) {
wrappedValue = '\'' + value + '\'';
}
$(eq).append($('<option></option>')
.attr('value', wrappedValue)
.text(wrappedValue));
});
$(eq).select2('destroy');
convertSelect(eq, true);
});
}

function addFilter(i, fieldPrefix) {
const isHaving = fieldPrefix === 'having';
const cp = $('#' + fieldPrefix + '0').clone();
$(cp).appendTo('#' + getPanelClass(fieldPrefix) + ' #filters');
$(cp).attr('id', fieldPrefix + filterCount);
$(cp).show();

const eqId = fieldPrefix + '_eq_' + filterCount;
const $eq = $(cp).find('#' + fieldPrefix + '_eq_0');
$eq.attr('id', eqId).attr('name', eqId);

const opId = fieldPrefix + '_op_' + filterCount;
const $op = $(cp).find('#' + fieldPrefix + '_op_0');
$op.attr('id', opId).attr('name', opId);

const colId = fieldPrefix + '_col_' + filterCount;
const $col = $(cp).find('#' + fieldPrefix + '_col_0');
$col.attr('id', colId).attr('name', colId);

// Set existing values
if (i !== undefined) {
$(cp).find('#' + fieldPrefix + '_eq_0').val(px.getParam(fieldPrefix + '_eq_' + i));
$(cp).find('#' + fieldPrefix + '_op_0').val(px.getParam(fieldPrefix + '_op_' + i));
$(cp).find('#' + fieldPrefix + '_col_0').val(px.getParam(fieldPrefix + '_col_' + i));
$op.val(px.getParam(fieldPrefix + '_op_' + i));
$col.val(px.getParam(fieldPrefix + '_col_' + i));

if (isHaving || !slice.filterSelectEnabled) {
$eq.val(px.getParam(fieldPrefix + '_eq_' + i));
} else {
insertFilterChoices(filterCount, fieldPrefix);
const eqVal = px.getParam(fieldPrefix + '_eq_' + i);
$eq.append($('<option selected></option>')
.attr('value', eqVal).text(eqVal));
}
}

if (slice.filterSelectEnabled && !isHaving) {
const currentFilter = filterCount;
$col.change(function () {
insertFilterChoices(currentFilter, fieldPrefix);
});
}

$(cp).find('select').select2();
$(cp).find('.remove').click(function () {
$(this)
.parent()
.parent()
.remove();
});
filterCount++;
}

function setFilters() {
Expand Down Expand Up @@ -244,52 +320,8 @@ function initExploreView() {
addFilter(undefined, 'having');
});

function createChoices(term, data) {
const filtered = $(data).filter(function () {
return this.text.localeCompare(term) === 0;
});
if (filtered.length === 0) {
return {
id: term,
text: term,
};
}
return {};
}

function initSelectionToValue(element, callback) {
callback({
id: element.val(),
text: element.val(),
});
}

$('.select2_freeform').each(function () {
const parent = $(this).parent();
const name = $(this).attr('name');
const l = [];
let selected = '';
for (let i = 0; i < this.options.length; i++) {
l.push({
id: this.options[i].value,
text: this.options[i].text,
});
if (this.options[i].selected) {
selected = this.options[i].value;
}
}
parent.append(
`<input class="${$(this).attr('class')}" ` +
`name="${name}" type="text" value="${selected}">`
);
$(`input[name='${name}']`).select2({
createSearchChoice: createChoices,
initSelection: initSelectionToValue,
dropdownAutoWidth: true,
multiple: false,
data: l,
});
$(this).remove();
convertSelect('#' + $(this).attr('id'), false);
});

function prepSaveDialog() {
Expand Down Expand Up @@ -387,9 +419,10 @@ exploreController = Object.assign({}, utils.controllerInterface, exploreControll
$(document).ready(function () {
const data = $('.slice').data('slice');

initExploreView();

slice = px.Slice(data, exploreController);
$('.slice').data('slice', slice);

initExploreView();

// call vis render method, which issues ajax
// calls render on the slice for the first time
Expand Down
7 changes: 7 additions & 0 deletions caravel/assets/javascripts/modules/caravel.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const px = function () {
const container = $(selector);
const sliceId = data.slice_id;
const origJsonEndpoint = data.json_endpoint;
const filterSelectEnabled = data.filter_select_enabled;
let dttm = 0;
const stopwatch = function () {
dttm += 10;
Expand All @@ -77,6 +78,7 @@ const px = function () {
data,
container,
containerId,
filterSelectEnabled,
selector,
querystring() {
const parser = document.createElement('a');
Expand Down Expand Up @@ -111,6 +113,11 @@ const px = function () {
endpoint += '&force=' + this.force;
return endpoint;
},
filterEndpoint(column) {
const parser = document.createElement('a');
parser.href = data.filter_endpoint;
return parser.pathname + column + '/' + this.querystring();
},
d3format(col, number) {
// uses the utils memoized d3format function and formats based on
// column level defined preferences
Expand Down
18 changes: 11 additions & 7 deletions caravel/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1088,13 +1088,17 @@ def add_to_form(attrs):
_('Filter 1'),
default=col_choices[0][0],
choices=col_choices))
setattr(QueryForm, field_prefix + '_op_' + str(i), SelectField(
_('Filter 1'),
default=op_choices[0][0],
choices=op_choices))
setattr(
QueryForm, field_prefix + '_eq_' + str(i),
TextField(_("Super"), default=''))
setattr(QueryForm, field_prefix + '_op_' + str(i),
SelectField(
_('Filter 1'),
default=op_choices[0][0],
choices=op_choices))
if viz.datasource.filter_select_enabled and not is_having_filter:
setattr(QueryForm, field_prefix + '_eq_' + str(i),
FreeFormSelectField(_("Super"), choices=[]))
else:
setattr(QueryForm, field_prefix + '_eq_' + str(i),
TextField(_("Super"), default=''))

if time_fields:
QueryForm.fieldsets = ({
Expand Down
26 changes: 26 additions & 0 deletions caravel/migrations/versions/6584a92c17a3_enable_filter_select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Enable Filter Select

Revision ID: 6584a92c17a3
Revises: c611f2b591b8
Create Date: 2016-11-07 17:25:33.081565

"""

# revision identifiers, used by Alembic.
revision = '6584a92c17a3'
down_revision = 'c611f2b591b8'

from alembic import op
import sqlalchemy as sa


def upgrade():
op.add_column('datasources', sa.Column('filter_select_enabled',
sa.Boolean(), default=False))
op.add_column('tables', sa.Column('filter_select_enabled',
sa.Boolean(), default=False))


def downgrade():
op.drop_column('tables', 'filter_select_enabled')
op.drop_column('datasources', 'filter_select_enabled')
Loading