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
202 changes: 120 additions & 82 deletions caravel/assets/javascripts/explore/explore.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,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 @@ -341,22 +311,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 initSelectionToValue(element, callback) {
callback({
id: element.val(),
text: element.val(),
});
}

function convertSelect(selectId) {
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,
initSelection: initSelectionToValue,
dropdownAutoWidth: true,
multiple: false,
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) {
$.each(data, function (key, value) {
$(eq).append($('<option></option>').attr('value', value).text(value));
});
$(eq).select2('destroy');
convertSelect(eq);
});
}

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 @@ -395,52 +471,16 @@ function initExploreView() {
queryAndSaveBtnsEl
);

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(),
});
}
$(window).bind('popstate', function () {
// Browser back button
const returnLocation = history.location || document.location;
// Could do something more lightweight here, but we're not optimizing
// for the use of the back button anyways
returnLocation.reload();
});

$('.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'));
});

function prepSaveDialog() {
Expand Down Expand Up @@ -489,13 +529,11 @@ function initExploreView() {

$(document).ready(function () {
const data = $('.slice').data('slice');

initExploreView();

slice = px.Slice(data);

$('.slice').data('slice', slice);

initExploreView();

// call vis render method, which issues ajax
query(false, false);

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 @@ -59,6 +59,7 @@ const px = function () {
const selector = '#' + containerId;
const container = $(selector);
const sliceId = data.sliceId;
const filterSelectEnabled = data.filter_select_enabled;
let dttm = 0;
const stopwatch = function () {
dttm += 10;
Expand All @@ -75,6 +76,7 @@ const px = function () {
data,
container,
containerId,
filterSelectEnabled,
selector,
querystring(params) {
const newParams = params || {};
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 @@ -1071,13 +1071,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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""datasource_table_filter_select

Revision ID: 17fc73813b42
Revises: 3c3ffe173e4f
Create Date: 2016-08-12 16:41:25.629004

"""

# revision identifiers, used by Alembic.
revision = '17fc73813b42'
down_revision = '3c3ffe173e4f'

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