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:
parent
496823e564
commit
80a199b4a9
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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"> OK </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();
|
||||||
|
});
|
||||||
|
};
|
@ -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([
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user