Started to hook up sorting. You can sort on one column right now but there's no indicator after you've done sorting.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@835 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-05-21 00:48:36 +00:00
parent 496823e564
commit 80a199b4a9
8 changed files with 263 additions and 15 deletions

View File

@ -8,6 +8,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONWriter; import org.json.JSONWriter;
import com.metaweb.gridworks.browsing.Engine; import com.metaweb.gridworks.browsing.Engine;
@ -20,6 +21,9 @@ import com.metaweb.gridworks.commands.Command;
import com.metaweb.gridworks.model.Project; import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.Record; import com.metaweb.gridworks.model.Record;
import com.metaweb.gridworks.model.Row; import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.sorting.SortingRecordVisitor;
import com.metaweb.gridworks.sorting.SortingRowVisitor;
import com.metaweb.gridworks.util.ParsingUtilities;
import com.metaweb.gridworks.util.Pool; import com.metaweb.gridworks.util.Pool;
public class GetRowsCommand extends Command { public class GetRowsCommand extends Command {
@ -46,25 +50,49 @@ public class GetRowsCommand extends Command {
JSONWriter writer = new JSONWriter(response.getWriter()); JSONWriter writer = new JSONWriter(response.getWriter());
writer.object(); writer.object();
RowWritingVisitor acc = new RowWritingVisitor(start, limit, writer, options); RowWritingVisitor rwv = new RowWritingVisitor(start, limit, writer, options);
JSONObject sortingJson = null;
try{
String json = request.getParameter("sorting");
sortingJson = (json == null) ? null :
ParsingUtilities.evaluateJsonStringToObject(json);
} catch (JSONException e) {
}
if (engine.getMode() == Mode.RowBased) { if (engine.getMode() == Mode.RowBased) {
FilteredRows filteredRows = engine.getAllFilteredRows(); FilteredRows filteredRows = engine.getAllFilteredRows();
RowVisitor visitor = rwv;
if (sortingJson != null) {
SortingRowVisitor srv = new SortingRowVisitor(visitor);
srv.initializeFromJSON(project, sortingJson);
visitor = srv;
}
writer.key("mode"); writer.value("row-based"); writer.key("mode"); writer.value("row-based");
writer.key("rows"); writer.array(); writer.key("rows"); writer.array();
filteredRows.accept(project, acc); filteredRows.accept(project, visitor);
writer.endArray(); writer.endArray();
writer.key("filtered"); writer.value(acc.total); writer.key("filtered"); writer.value(rwv.total);
writer.key("total"); writer.value(project.rows.size()); writer.key("total"); writer.value(project.rows.size());
} else { } else {
FilteredRecords filteredRecords = engine.getFilteredRecords(); FilteredRecords filteredRecords = engine.getFilteredRecords();
RecordVisitor visitor = rwv;
if (sortingJson != null) {
SortingRecordVisitor srv = new SortingRecordVisitor(visitor);
srv.initializeFromJSON(project, sortingJson);
visitor = srv;
}
writer.key("mode"); writer.value("record-based"); writer.key("mode"); writer.value("record-based");
writer.key("rows"); writer.array(); writer.key("rows"); writer.array();
filteredRecords.accept(project, acc); filteredRecords.accept(project, visitor);
writer.endArray(); writer.endArray();
writer.key("filtered"); writer.value(acc.total); writer.key("filtered"); writer.value(rwv.total);
writer.key("total"); writer.value(project.recordModel.getRecordCount()); writer.key("total"); writer.value(project.recordModel.getRecordCount());
} }

View File

@ -26,6 +26,10 @@ abstract public class BaseSorter {
} }
public Object getKey(Project project, Object o, int index) { public Object getKey(Project project, Object o, int index) {
while (index >= _keys.size()) {
_keys.add(null);
}
Object[] keys = _keys.get(index); Object[] keys = _keys.get(index);
if (keys == null) { if (keys == null) {
keys = makeKeys(project, o, index); keys = makeKeys(project, o, index);

View File

@ -6,22 +6,23 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import com.metaweb.gridworks.browsing.RecordVisitor; import com.metaweb.gridworks.browsing.RecordVisitor;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.model.Project; import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.Record; import com.metaweb.gridworks.model.Record;
import com.metaweb.gridworks.sorting.Criterion.KeyMaker; import com.metaweb.gridworks.sorting.Criterion.KeyMaker;
public class SortingRecordVisitor extends BaseSorter implements RecordVisitor { public class SortingRecordVisitor extends BaseSorter implements RecordVisitor {
final protected RowVisitor _visitor; final protected RecordVisitor _visitor;
protected List<Record> _records; protected List<Record> _records;
public SortingRecordVisitor(RowVisitor visitor) { public SortingRecordVisitor(RecordVisitor visitor) {
_visitor = visitor; _visitor = visitor;
} }
@Override @Override
public void start(Project project) { public void start(Project project) {
_records = new ArrayList<Record>(project.recordModel.getRecordCount()); int count = project.recordModel.getRecordCount();
_records = new ArrayList<Record>(count);
_keys = new ArrayList<Object[]>(count);
} }
@Override @Override
@ -42,6 +43,10 @@ public class SortingRecordVisitor extends BaseSorter implements RecordVisitor {
} }
}.init(project)); }.init(project));
for (Record record : _records) {
_visitor.visit(project, record);
}
_visitor.end(project); _visitor.end(project);
} }

View File

@ -30,7 +30,9 @@ public class SortingRowVisitor extends BaseSorter implements RowVisitor {
@Override @Override
public void start(Project project) { public void start(Project project) {
_indexedRows = new ArrayList<IndexedRow>(project.rows.size()); int count = project.rows.size();
_indexedRows = new ArrayList<IndexedRow>(count);
_keys = new ArrayList<Object[]>(count);
} }
@Override @Override
@ -51,6 +53,10 @@ public class SortingRowVisitor extends BaseSorter implements RowVisitor {
} }
}.init(project)); }.init(project));
for (IndexedRow indexedRow : _indexedRows) {
_visitor.visit(project, indexedRow.index, indexedRow.row);
}
_visitor.end(project); _visitor.end(project);
} }

View File

@ -337,10 +337,17 @@ Gridworks.preparePool = function(pool) {
} }
}; };
Gridworks.fetchRows = function(start, limit, onDone) { Gridworks.fetchRows = function(start, limit, onDone, sorting) {
var body = {
engine: JSON.stringify(ui.browsingEngine.getJSON())
};
if (sorting) {
body.sorting = JSON.stringify(sorting)
}
$.post( $.post(
"/command/get-rows?" + $.param({ project: theProject.id, start: start, limit: limit }), "/command/get-rows?" + $.param({ project: theProject.id, start: start, limit: limit }),
{ engine: JSON.stringify(ui.browsingEngine.getJSON()) }, body,
function(data) { function(data) {
theProject.rowModel = data; theProject.rowModel = data;

View File

@ -313,6 +313,10 @@ DataTableColumnHeaderUI.prototype._createMenuForColumnHeader = function(elmt) {
] ]
}, },
{}, {},
{
label: "Sort",
submenu: this.createSortingMenu()
},
{ {
label: "View", label: "View",
tooltip: "Collapse/expand columns to make viewing the data more convenient", tooltip: "Collapse/expand columns to make viewing the data more convenient",
@ -538,6 +542,40 @@ DataTableColumnHeaderUI.prototype._createMenuForColumnHeader = function(elmt) {
], elmt, { width: "120px", horizontal: false }); ], elmt, { width: "120px", horizontal: false });
}; };
DataTableColumnHeaderUI.prototype.createSortingMenu = function() {
var self = this;
var criterion = this._dataTableView._getSortingCriterionForColumn(this._column.name);
var criteriaCount = this._dataTableView._getSortingCriteriaCount();
var hasOtherCriteria = criterion == null ? (criteriaCount > 0) : criteriaCount > 1;
var items = [
{
"label": "Sort ...",
"click": function() {
self._showSortingCriterion(criterion, hasOtherCriteria)
}
}
];
if (criterion != null) {
items.push({
"label": "Reverse",
"click": function() {
criterion.reverse = !criterion.reverse;
self._dataTableView._addSortingCriterion(criterion);
}
});
items.push({
"label": "Un-sort",
"click": function() {
self._dataTableView._removeSortingCriterionOfColumn(criterion.column);
}
});
}
return items;
};
DataTableColumnHeaderUI.prototype._doFilterByExpressionPrompt = function(expression, type) { DataTableColumnHeaderUI.prototype._doFilterByExpressionPrompt = function(expression, type) {
var self = this; var self = this;
DataTableView.promptExpressionOnVisibleRows( DataTableView.promptExpressionOnVisibleRows(
@ -1080,3 +1118,111 @@ DataTableColumnHeaderUI.prototype._doSplitColumn = function() {
footerElmts.cancelButton.click(dismiss); footerElmts.cancelButton.click(dismiss);
}; };
DataTableColumnHeaderUI.prototype._showSortingCriterion = function(criterion, hasOtherCriteria) {
var self = this;
criterion = criterion || {
column: this._column.name,
valueType: "string",
caseSensitive: false,
errorPosition: 1,
blankPosition: 2
};
var frame = DialogSystem.createDialog();
frame.width("400px");
var header = $('<div></div>').addClass("dialog-header").text("Sort by " + this._column.name).appendTo(frame);
var body = $('<div></div>').addClass("dialog-body").appendTo(frame);
var footer = $('<div></div>').addClass("dialog-footer").appendTo(frame);
body.html(
'<div class="grid-layout layout-normal layout-full "><table>' +
'<tr>' +
'<td>Sort cell values as</td>' +
'<td>Position blanks and errors</td>' +
'</tr>' +
'<tr>' +
'<td>' +
'<div class="grid-layout layout-tightest grid-layout-for-text" bind="valueTypeOptions"><table>' +
'<tr>' +
'<td width="1"><input type="radio" name="sorting-dialog-value-type" value="string" /></td>' +
'<td>text <input type="checkbox" class="inline" bind="caseSensitiveCheckbox" /> case-sensitive</td>' +
'</tr>' +
'<tr>' +
'<td width="1"><input type="radio" name="sorting-dialog-value-type" value="number" /></td>' +
'<td>numbers</td>' +
'</tr>' +
'<tr>' +
'<td width="1"><input type="radio" name="sorting-dialog-value-type" value="date" /></td>' +
'<td>dates</td>' +
'</tr>' +
'<tr>' +
'<td width="1"><input type="radio" name="sorting-dialog-value-type" value="boolean" /></td>' +
'<td>booleans</td>' +
'</tr>' +
'</table></div>' +
'</td>' +
'<td>' +
'<ul>' +
'<li>Valid Values</li>' +
'<li>Blanks</li>' +
'<li>Errors</li>' +
'</ul>' +
'<p>Drag and drop to re-order</p>' +
'</td>' +
'</tr>' +
'<tr><td colspan="2" bind="directionOptions">' +
'<input type="radio" class="inline" name="sorting-dialog-direction" value="forward" /><label>forward</label> ' +
'<input type="radio" class="inline" name="sorting-dialog-direction" value="reverse" /><label>reverse</label> ' +
'<span bind="sortAloneContainer" style="display:none;"><input type="checkbox" class="inline" /><label>sort by this column alone</label></span>' +
'</td></tr>' +
'</table></div>'
);
var bodyElmts = DOM.bind(body);
bodyElmts.valueTypeOptions.find("input[type='radio'][value='" + criterion.valueType + "']")
.attr("checked", "checked");
if (criterion.valueType == "string" && criterion.caseSensitive) {
bodyElmts.caseSensitiveCheckbox.attr("checked", "checked");
}
bodyElmts.directionOptions.find("input[type='radio'][value='" + (criterion.reverse ? "reverse" : "forward") + "']")
.attr("checked", "checked");
if (hasOtherCriteria) {
bodyElmts.sortAloneContainer.show();
}
footer.html(
'<button bind="okButton">&nbsp;&nbsp;OK&nbsp;&nbsp;</button>' +
'<button bind="cancelButton">Cancel</button>'
);
var footerElmts = DOM.bind(footer);
var level = DialogSystem.showDialog(frame);
var dismiss = function() {
DialogSystem.dismissUntil(level - 1);
};
footerElmts.cancelButton.click(dismiss);
footerElmts.okButton.click(function() {
var criterion2 = {
column: self._column.name,
valueType: bodyElmts.valueTypeOptions.find("input[type='radio']:checked")[0].value,
reverse: bodyElmts.directionOptions.find("input[type='radio']:checked")[0].value == "reverse"
};
if (criterion2.valueType == "string") {
criterion2.caseSensitive = bodyElmts.caseSensitiveCheckbox[0].checked;
}
self._dataTableView._addSortingCriterion(
criterion2, bodyElmts.sortAloneContainer.find("input")[0].checked);
dismiss();
});
};

View File

@ -3,6 +3,7 @@ function DataTableView(div) {
this._pageSize = 20; this._pageSize = 20;
this._showRecon = true; this._showRecon = true;
this._collapsedColumnNames = {}; this._collapsedColumnNames = {};
this._sorting = { criteria: [] };
this._showRows(0); this._showRows(0);
} }
@ -333,7 +334,7 @@ DataTableView.prototype._showRows = function(start, onDone) {
if (onDone) { if (onDone) {
onDone(); onDone();
} }
}); }, this._sorting);
}; };
DataTableView.prototype._onClickPreviousPage = function(elmt, evt) { DataTableView.prototype._onClickPreviousPage = function(elmt, evt) {
@ -352,6 +353,54 @@ DataTableView.prototype._onClickLastPage = function(elmt, evt) {
this._showRows(Math.floor(theProject.rowModel.filtered / this._pageSize) * this._pageSize); this._showRows(Math.floor(theProject.rowModel.filtered / this._pageSize) * this._pageSize);
}; };
DataTableView.prototype._getSortingCriteriaCount = function() {
return this._sorting.criteria.length;
};
DataTableView.prototype._sortedByColumn = function(columnName) {
for (var i = 0; i < this._sorting.criteria.length; i++) {
if (this._sorting.criteria[i].column == columnName) {
return true;
}
}
return false;
};
DataTableView.prototype._getSortingCriterionForColumn = function(columnName) {
for (var i = 0; i < this._sorting.criteria.length; i++) {
if (this._sorting.criteria[i].column == columnName) {
return this._sorting.criteria[i];
}
}
return null;
};
DataTableView.prototype._removeSortingCriterionOfColumn = function(columnName) {
for (var i = 0; i < this._sorting.criteria.length; i++) {
if (this._sorting.criteria[i].column == columnName) {
this._sorting.criteria.splice(i, 1);
break;
}
}
this.update();
};
DataTableView.prototype._addSortingCriterion = function(criterion, alone) {
if (alone) {
this._sorting.criteria = [];
} else {
for (var i = 0; i < this._sorting.criteria.length; i++) {
if (this._sorting.criteria[i].column == criterion.column) {
this._sorting.criteria[i] = criterion;
this.update();
return;
}
}
}
this._sorting.criteria.push(criterion);
this.update();
};
DataTableView.prototype._createMenuForAllColumns = function(elmt) { DataTableView.prototype._createMenuForAllColumns = function(elmt) {
self = this; self = this;
MenuSystem.createAndShowStandardMenu([ MenuSystem.createAndShowStandardMenu([

View File

@ -29,6 +29,9 @@ div.grid-layout > table > tbody > tr > th, div.grid-layout > table > tbody > tr
text-align: left; text-align: left;
vertical-align: baseline; vertical-align: baseline;
} }
div.grid-layout.grid-layout-for-text > table > tbody > tr > th, div.grid-layout.grid-layout-for-text > table > tbody > tr > td {
vertical-align: middle;
}
div.grid-layout.layout-normal { div.grid-layout.layout-normal {
margin: -10px; margin: -10px;
} }