Skip to content

Client-side filtering and table related cleanup #147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 16, 2016
Merged
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
2 changes: 1 addition & 1 deletion src/firefly/html/firefly.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<script type="text/javascript" src="firefly_loader.js"></script>
</head>

<body style="margin: 0px;">
<body style="margin: 0; background-color: rgb(200,200,200)">
<!-- attached loaction for firefly app -->
<div id='app'/>

Expand Down
27 changes: 19 additions & 8 deletions src/firefly/js/charts/ui/ColSelectView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import DialogRootContainer from '../../ui/DialogRootContainer.jsx';
import {PopupPanel} from '../../ui/PopupPanel.jsx';
import {dispatchTableRemove} from '../../tables/TablesCntlr';

import {BasicTable} from '../../tables/ui/BasicTable.jsx';
import {getTblById} from '../../tables/TableUtil.js';
import {TablePanel} from '../../tables/ui/TablePanel.jsx';
import {getTblById, calcColumnWidths} from '../../tables/TableUtil.js';
import {dispatchShowDialog, dispatchHideDialog, isDialogVisible} from '../../core/ComponentCntlr.js';
import CompleteButton from '../../ui/CompleteButton.jsx';
//import HelpIcon from '../../ui/HelpIcon.jsx';
Expand All @@ -25,7 +25,7 @@ const popupPanelResizableStyle = {


//define the table style only in the table div
const tableStyle = {boxSizing: 'border-box', paddingLeft:5,paddingRight:5, width: '100%', height: 'calc(100% - 70px)', overflow: 'hidden', flexGrow: 1, display: 'flex', resize:'none'};
const tableStyle = {boxSizing: 'border-box', width: '100%', height: 'calc(100% - 40px)', overflow: 'hidden', resize:'none'};

//define the complete button
const closeButtonStyle = {'textAlign': 'center', display: 'inline-block', height:40, marginTop:10, width: '90%'};
Expand All @@ -45,10 +45,10 @@ export function showColSelectPopup(colValStats,onColSelected,popupTitle,buttonTe

// make a local table for plot column selection panel
var columns = [
{name: 'Name',visibility: 'show', prefWidth: 12},
{name: 'Unit',visibility: 'show', prefWidth: 8},
{name: 'Type',visibility: 'show', prefWidth: 8},
{name: 'Description',visibility: 'show', prefWidth: 60}
{name: 'Name'},
{name: 'Unit'},
{name: 'Type'},
{name: 'Description'}
];
var data = [];
for (var i = 0; i < colValStats.length; i++) {
Expand All @@ -59,6 +59,14 @@ export function showColSelectPopup(colValStats,onColSelected,popupTitle,buttonTe
colValStats[i].descr
];
}

const widths = calcColumnWidths(columns, data);
columns[0].prefWidth = Math.min(widths[0], 30); // adjust width of column for optimum display.
columns[1].prefWidth = Math.min(widths[1], 15);
columns[2].prefWidth = Math.min(widths[2], 15);
columns[3].prefWidth = Math.min(widths[3], 100);


const request = {pageSize:10000};
var tableModel = {totalRows: data.length, request, tbl_id:TBL_ID, tableData: {columns, data }, highlightedRow: hlRowNum};

Expand Down Expand Up @@ -100,9 +108,12 @@ function renderTable(tableModel,popupId) {

return (
<div style={tableStyle}>
<BasicTable
<TablePanel
key={popupId}
tableModel={tableModel}
showToolbar={false}
selectable={false}
border={false}
/>
</div>
);
Expand Down
90 changes: 65 additions & 25 deletions src/firefly/js/tables/FilterInfo.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
/**
* Created by loi on 1/15/16.
*/


/*
* License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
*/
const op_regex = new RegExp('(!=|>=|<=|<|>|=|like|in)', 'i');
const cond_regex = new RegExp('^' + op_regex.source + '\\s+(.+)', 'i');
const filter_regex = new RegExp('(\\S+)\\s+' + op_regex.source + '\\s+(.+)', 'i');

import {getColumnIdx, getColumn} from './TableUtil.js';
const cond_regex = new RegExp('(!=|>=|<=|<|>|=|like|in)\\s*(.+)');
const cond_only_regex = new RegExp('^' + cond_regex.source, 'i');
const filter_regex = new RegExp('(\\S+)\\s*' + cond_regex.source, 'i');

export const FILTER_CONDITION_TTIPS =
`Valid values are one of (=, >, <, !=, >=, <=, LIKE) followed by a value separated by a space.
Expand Down Expand Up @@ -47,8 +44,8 @@ export class FilterInfo {
var filterInfo = new FilterInfo();
if (filterString) {
filterString && filterString.split(';').forEach( (v) => {
const parts = v.trim().match(filter_regex);
if (parts.length > 3) filterInfo.addFilter(parts[1], `${parts[2]} ${parts[3]}`);
const [, cname, op, val] = v.trim().match(filter_regex) || [];
if (cname) filterInfo.addFilter(cname, `${op} ${val}`);
});
}
return filterInfo;
Expand All @@ -63,8 +60,8 @@ export class FilterInfo {
static autoCorrect(conditions) {
if (conditions) {
const parts = conditions.split(';').map( (v) => {
const opVal = v.replace(/\(|\)| /g, '').split(op_regex);
var [op, val] = opVal.length > 1 ? [ opVal[1], opVal[2] ] : [ 'like', opVal[0] ]; // defualt to 'like' if no operators found
var [, op, val] = v.trim().replace(/[()]/g, '').match(cond_only_regex) || [];
[op, val] = op ? [op, val] : [ 'like', v.trim() ]; // defualt to 'like' if no operators found
val = op.toLowerCase() === 'in' ? `(${val})` : val; // add parentheses when 'in' is used.
return `${op} ${val}`;
});
Expand All @@ -83,9 +80,9 @@ export class FilterInfo {
static autoCorrectFilter(filterInfo) {
if (filterInfo) {
const filters = filterInfo.split(';').map( (v) => {
const parts = v.split(op_regex).map( (s) => s.trim());
if (parts.length != 3) return v;
return `${parts[0]} ${FilterInfo.autoCorrect(parts[1]+parts[2])}`;
const [, cname, op, val] = v.trim().match(filter_regex) || [];
if (!cname) return v;
return `${cname} ${FilterInfo.autoCorrect(op + ' ' + val)}`;
});
return filters.join(';');
} else {
Expand All @@ -100,7 +97,7 @@ export class FilterInfo {
*/
static isConditionValid(conditions) {
return !conditions || conditions.split(';').reduce( (rval, v) => {
return rval && cond_regex.test(v.trim());
return rval && cond_only_regex.test(v.trim());
}, true);
}

Expand All @@ -117,19 +114,19 @@ export class FilterInfo {

/**
* validate the filterInfo string
* @param conditions
* @param columns array of column definitions
* @param {string} filterInfo
* @param {TableColumn[]} columns array of column definitions
* @returns {[boolean, string]} isValid plus an error message if isValid is false.
*/
static isValid(filterInfo, columns = []) {
const rval = [true, ''];
const allowCols = columns.concat({name:'ROWID'});
if (filterInfo && filterInfo.trim().length > 0) {
return filterInfo.split(';').reduce( ([isValid, msg], v) => {
const parts = v.split(op_regex).map( (s) => s.trim());
if (parts.length !== 3) {
const [, cname] = v.trim().match(filter_regex) || [];
if (!cname) {
msg += `\n"${v}" is not a valid filter.`;
} else if (!allowCols.some( (col) => col.name === parts[0])) {
} else if (!allowCols.some( (c) => c.name === cname)) {
msg +=`\n"${v}" column not found.\n`;
}
return [!msg, msg];
Expand All @@ -141,8 +138,8 @@ export class FilterInfo {

/**
* validator for free-form filters field
* @param filterInfo string serialized filter list
* @param columns array of column definitions
* @param {TableColumn[]} columns array of column definitions
* @param {string} filterInfo
* @returns {{valid: boolean, value: (string|*), message: string}}
*/
static validator(columns, filterInfo) {
Expand All @@ -151,6 +148,49 @@ export class FilterInfo {
return {valid, value: filterInfo, message};
}

/**
* returns a comparator function that takes a row(string[]) as parameter.
* This comparator will returns true if the given row passes the given filterStr.
* @param {string} filterStr
* @param {TableModel} tableModel
* @returns {function(): boolean}
*/
static createComparator(filterStr, tableModel) {
var [ , cname, op, val] =filterStr.match(filter_regex) || [];
if (!cname) return () => false; // bad filter.. returns nothing.

op = op.toLowerCase();
val = val.toLowerCase();
const cidx = getColumnIdx(tableModel, cname);
const col = getColumn(tableModel, cname);
val = op === 'in' ? val.replace(/[()]/g, '').split(',').map((s) => s.trim()) : val;
return (row) => {
var compareTo = row[cidx].toLowerCase();
if (!['in','like'].includes(op) && col.type.match(/^[dfil]/)) { // int, float, double, long .. or their short form.
val = Number(val);
compareTo = Number(compareTo);
}
switch (op) {
case '!=' :
return compareTo !== val;
case '>=' :
return compareTo >= val;
case '<=' :
return compareTo <= val;
case '>' :
return compareTo > val;
case '=' :
return compareTo === val;
case 'like' :
return compareTo.includes(val);
case 'in' :
return val.includes(compareTo);
default :
return false;
}
};
}

serialize() {
return Object.keys(this).reduce( (rval, key) => {
this[key].split(';').forEach((v) => {
Expand All @@ -174,8 +214,8 @@ export class FilterInfo {
Reflect.deleteProperty(this, colName);
if (conditions) {
conditions.split(';').forEach( (v) => {
const parts = v.trim().match(cond_regex);
if (parts.length > 2) this.addFilter(colName, `${parts[1]} ${parts[2]}`);
const [, op, val] = v.trim().match(cond_only_regex) || [];
if (op) this.addFilter(colName, `${op} ${val}`);
});
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/firefly/js/tables/TableConnector.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class TableConnector {
var {tableModel, request} = TblUtil.getTblInfoById(this.tbl_id);
if (this.origTableModel) {
tableModel = TblUtil.sortTable(this.origTableModel, sortInfoString);
flux.process({type: TblCntlr.TABLE_REPLACE, payload: tableModel});
TblCntlr.dispatchTableReplace(tableModel);
} else {
request = Object.assign({}, request, {sortInfo: sortInfoString});
TblCntlr.dispatchTableSort(request);
Expand All @@ -32,7 +32,8 @@ export class TableConnector {
onFilter(filterIntoString) {
var {tableModel, request} = TblUtil.getTblInfoById(this.tbl_id);
if (this.origTableModel) {
// not implemented yet
tableModel = filterIntoString ? TblUtil.filterTable(this.origTableModel, filterIntoString) : this.origTableModel;
TblCntlr.dispatchTableReplace(tableModel);
} else {
request = Object.assign({}, request, {filters: filterIntoString});
TblCntlr.dispatchTableFilter(request);
Expand Down
Loading