Added new command Transpose Cells in Columns into Rows.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@1111 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-07-30 02:25:58 +00:00
parent a192674118
commit ee14955605
7 changed files with 472 additions and 0 deletions

View File

@ -71,6 +71,7 @@ public class GridworksServlet extends Butterfly {
{"mass-edit", "com.metaweb.gridworks.commands.cell.MassEditCommand"}, {"mass-edit", "com.metaweb.gridworks.commands.cell.MassEditCommand"},
{"join-multi-value-cells", "com.metaweb.gridworks.commands.cell.JoinMultiValueCellsCommand"}, {"join-multi-value-cells", "com.metaweb.gridworks.commands.cell.JoinMultiValueCellsCommand"},
{"split-multi-value-cells", "com.metaweb.gridworks.commands.cell.SplitMultiValueCellsCommand"}, {"split-multi-value-cells", "com.metaweb.gridworks.commands.cell.SplitMultiValueCellsCommand"},
{"transpose-columns-into-rows", "com.metaweb.gridworks.commands.cell.TransposeColumnsIntoRowsCommand"},
{"add-column", "com.metaweb.gridworks.commands.column.AddColumnCommand"}, {"add-column", "com.metaweb.gridworks.commands.column.AddColumnCommand"},
{"remove-column", "com.metaweb.gridworks.commands.column.RemoveColumnCommand"}, {"remove-column", "com.metaweb.gridworks.commands.column.RemoveColumnCommand"},

View File

@ -0,0 +1,42 @@
package com.metaweb.gridworks.commands.cell;
import java.io.IOException;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.metaweb.gridworks.commands.Command;
import com.metaweb.gridworks.model.AbstractOperation;
import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.operations.cell.TransposeColumnsIntoRowsOperation;
import com.metaweb.gridworks.process.Process;
public class TransposeColumnsIntoRowsCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
Project project = getProject(request);
String startColumnName = request.getParameter("startColumnName");
int columnCount = Integer.parseInt(request.getParameter("columnCount"));
String combinedColumnName = request.getParameter("combinedColumnName");
boolean prependColumnName = Boolean.parseBoolean(request.getParameter("prependColumnName"));
String separator = request.getParameter("separator");
boolean ignoreBlankCells = Boolean.parseBoolean(request.getParameter("ignoreBlankCells"));
AbstractOperation op = new TransposeColumnsIntoRowsOperation(
startColumnName, columnCount, combinedColumnName, prependColumnName, separator, ignoreBlankCells);
Process process = op.createProcess(project, new Properties());
performProcessAndRespond(request, response, project, process);
} catch (Exception e) {
respondException(response, e);
}
}
}

View File

@ -0,0 +1,139 @@
package com.metaweb.gridworks.model.changes;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import com.metaweb.gridworks.history.Change;
import com.metaweb.gridworks.model.Column;
import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.util.Pool;
public class MassRowColumnChange implements Change {
final protected List<Column> _newColumns;
final protected List<Row> _newRows;
protected List<Column> _oldColumns;
protected List<Row> _oldRows;
public MassRowColumnChange(List<Column> newColumns, List<Row> newRows) {
_newColumns = newColumns;
_newRows = newRows;
}
public void apply(Project project) {
synchronized (project) {
_oldColumns = new ArrayList<Column>(project.columnModel.columns);
_oldRows = new ArrayList<Row>(project.rows);
project.columnModel.columns.clear();
project.columnModel.columns.addAll(_newColumns);
project.rows.clear();
project.rows.addAll(_newRows);
project.update();
}
}
public void revert(Project project) {
synchronized (project) {
project.columnModel.columns.clear();
project.columnModel.columns.addAll(_oldColumns);
project.rows.clear();
project.rows.addAll(_oldRows);
project.update();
}
}
public void save(Writer writer, Properties options) throws IOException {
writer.write("newColumnCount="); writer.write(Integer.toString(_newColumns.size())); writer.write('\n');
for (Column column : _newColumns) {
column.save(writer);
writer.write('\n');
}
writer.write("oldColumnCount="); writer.write(Integer.toString(_oldColumns.size())); writer.write('\n');
for (Column column : _oldColumns) {
column.save(writer);
writer.write('\n');
}
writer.write("newRowCount="); writer.write(Integer.toString(_newRows.size())); writer.write('\n');
for (Row row : _newRows) {
row.save(writer, options);
writer.write('\n');
}
writer.write("oldRowCount="); writer.write(Integer.toString(_oldRows.size())); writer.write('\n');
for (Row row : _oldRows) {
row.save(writer, options);
writer.write('\n');
}
writer.write("/ec/\n"); // end of change marker
}
static public Change load(LineNumberReader reader, Pool pool) throws Exception {
List<Column> oldColumns = null;
List<Column> newColumns = null;
List<Row> oldRows = null;
List<Row> newRows = null;
String line;
while ((line = reader.readLine()) != null && !"/ec/".equals(line)) {
int equal = line.indexOf('=');
CharSequence field = line.subSequence(0, equal);
if ("oldRowCount".equals(field)) {
int count = Integer.parseInt(line.substring(equal + 1));
oldRows = new ArrayList<Row>(count);
for (int i = 0; i < count; i++) {
line = reader.readLine();
if (line != null) {
oldRows.add(Row.load(line, pool));
}
}
} else if ("newRowCount".equals(field)) {
int count = Integer.parseInt(line.substring(equal + 1));
newRows = new ArrayList<Row>(count);
for (int i = 0; i < count; i++) {
line = reader.readLine();
if (line != null) {
newRows.add(Row.load(line, pool));
}
}
} else if ("oldColumnCount".equals(field)) {
int count = Integer.parseInt(line.substring(equal + 1));
oldColumns = new ArrayList<Column>(count);
for (int i = 0; i < count; i++) {
line = reader.readLine();
if (line != null) {
oldColumns.add(Column.load(line));
}
}
} else if ("newColumnCount".equals(field)) {
int count = Integer.parseInt(line.substring(equal + 1));
newColumns = new ArrayList<Column>(count);
for (int i = 0; i < count; i++) {
line = reader.readLine();
if (line != null) {
newColumns.add(Column.load(line));
}
}
}
}
MassRowColumnChange change = new MassRowColumnChange(newColumns, newRows);
change._oldColumns = oldColumns;
change._oldRows = oldRows;
return change;
}
}

View File

@ -0,0 +1,171 @@
package com.metaweb.gridworks.operations.cell;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONWriter;
import com.metaweb.gridworks.history.HistoryEntry;
import com.metaweb.gridworks.model.AbstractOperation;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Column;
import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.model.changes.MassRowColumnChange;
import com.metaweb.gridworks.operations.OperationRegistry;
public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
final protected String _startColumnName;
final protected int _columnCount;
final protected String _combinedColumnName;
final protected boolean _prependColumnName;
final protected String _separator;
final protected boolean _ignoreBlankCells;
static public AbstractOperation reconstruct(Project project, JSONObject obj) throws Exception {
return new TransposeColumnsIntoRowsOperation(
obj.getString("startColumnName"),
obj.getInt("columnCount"),
obj.getString("combinedColumnName"),
obj.getBoolean("prependColumnName"),
obj.getString("separator"),
obj.getBoolean("ignoreBlankCells")
);
}
public TransposeColumnsIntoRowsOperation(
String startColumnName,
int columnCount,
String combinedColumnName,
boolean prependColumnName,
String separator,
boolean ignoreBlankCells
) {
_startColumnName = startColumnName;
_columnCount = columnCount;
_combinedColumnName = combinedColumnName;
_prependColumnName = prependColumnName;
_separator = separator;
_ignoreBlankCells = ignoreBlankCells;
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("op"); writer.value(OperationRegistry.s_opClassToName.get(this.getClass()));
writer.key("description"); writer.value("Transpose cells in " + _columnCount + " column(s) starting with " + _startColumnName + " into rows");
writer.key("startColumnName"); writer.value(_startColumnName);
writer.key("columnCount"); writer.value(_columnCount);
writer.key("combinedColumnName"); writer.value(_combinedColumnName);
writer.key("prependColumnName"); writer.value(_prependColumnName);
writer.key("separator"); writer.value(_separator);
writer.key("ignoreBlankCells"); writer.value(_ignoreBlankCells);
writer.endObject();
}
protected String getBriefDescription(Project project) {
return "Transpose cells in " + _columnCount + " column(s) starting with " + _startColumnName + " into rows";
}
@Override
protected HistoryEntry createHistoryEntry(Project project, long historyEntryID) throws Exception {
List<Column> newColumns = new ArrayList<Column>();
List<Column> oldColumns = project.columnModel.columns;
int columnsLeftToTranspose = _columnCount;
int startColumnIndex = oldColumns.size();
for (int c = 0; c < oldColumns.size(); c++) {
Column column = oldColumns.get(c);
if (columnsLeftToTranspose == 0) {
// This column is beyond the columns to transpose
Column newColumn = new Column(newColumns.size(), column.getOriginalHeaderLabel());
newColumn.setName(column.getName());
newColumns.add(newColumn);
} else if (columnsLeftToTranspose < _columnCount) {
// This column is a column to transpose, but not the first
// nothing to do
columnsLeftToTranspose--;
} else if (_startColumnName.equals(column.getName())) {
// This is the first column to transpose
startColumnIndex = c;
String columnName = _combinedColumnName != null && _combinedColumnName.length() > 0 ? _combinedColumnName : column.getName();
Column newColumn = new Column(newColumns.size(), columnName);
newColumns.add(newColumn);
columnsLeftToTranspose--;
} else {
// This column is before all columns to transpose
Column newColumn = new Column(newColumns.size(), column.getOriginalHeaderLabel());
newColumn.setName(column.getName());
newColumns.add(newColumn);
}
}
List<Row> oldRows = project.rows;
List<Row> newRows = new ArrayList<Row>(oldRows.size() * _columnCount);
for (int r = 0; r < oldRows.size(); r++) {
Row oldRow = project.rows.get(r);
Row firstNewRow = new Row(newColumns.size());
newRows.add(firstNewRow);
int transposedCells = 0;
for (int c = 0; c < oldColumns.size(); c++) {
Column column = oldColumns.get(c);
Cell cell = oldRow.getCell(column.getCellIndex());
if (c < startColumnIndex) {
firstNewRow.setCell(c, cell);
} else if (c == startColumnIndex || c < startColumnIndex + _columnCount) {
Cell newCell;
if (cell == null || cell.value == null) {
if (_prependColumnName && !_ignoreBlankCells) {
newCell = new Cell(column.getName() + _separator, null);
} else {
continue;
}
} else if (_prependColumnName) {
newCell = new Cell(column.getName() + _separator + cell.value, null);
} else {
newCell = cell;
}
if (transposedCells == 0) {
firstNewRow.setCell(startColumnIndex, newCell);
} else {
Row newRow = new Row(newColumns.size());
newRow.setCell(startColumnIndex, newCell);
newRows.add(newRow);
}
transposedCells++;
} else {
firstNewRow.setCell(c - _columnCount + 1, cell);
}
}
}
return new HistoryEntry(
historyEntryID,
project,
getBriefDescription(null),
this,
new MassRowColumnChange(newColumns, newRows)
);
}
}

View File

@ -127,6 +127,10 @@ BrowsingEngine.prototype._updateFacetOrder = function() {
this._facets = newFacets; this._facets = newFacets;
}; };
BrowsingEngine.prototype.getMode = function() {
return this._elmts.modeSelectors.find("input:checked")[0].value;
};
BrowsingEngine.prototype.getJSON = function(keepUnrestrictedFacets, except) { BrowsingEngine.prototype.getJSON = function(keepUnrestrictedFacets, except) {
var a = { var a = {
facets: [], facets: [],

View File

@ -320,6 +320,15 @@ DataTableColumnHeaderUI.prototype._createMenuForColumnHeader = function(elmt) {
} }
] ]
}, },
{
label: "Transpose",
submenu: [
{
label: "Cells Across Columns into Rows",
click: function() { self._doTransposeColumnsIntoRows(); }
}
]
},
{}, {},
( (
this._dataTableView._getSortingCriterionForColumn(this._column.name) == null ? this._dataTableView._getSortingCriterionForColumn(this._column.name) == null ?
@ -1083,6 +1092,74 @@ DataTableColumnHeaderUI.prototype._doSplitColumn = function() {
footerElmts.cancelButton.click(dismiss); footerElmts.cancelButton.click(dismiss);
}; };
DataTableColumnHeaderUI.prototype._doTransposeColumnsIntoRows = function() {
var self = this;
var dialog = $(DOM.loadHTML("core", "scripts/views/data-table/transpose-columns-into-rows.html"));
var elmts = DOM.bind(dialog);
elmts.dialogHeader.text('Transpose Cells Across Columns into Rows');
var level = DialogSystem.showDialog(dialog);
var dismiss = function() {
DialogSystem.dismissUntil(level - 1);
};
var columns = theProject.columnModel.columns;
elmts.cancelButton.click(function() { dismiss(); });
elmts.okButton.click(function() {
var config = {
startColumnName: elmts.fromColumnSelect[0].value,
columnCount: elmts.toColumnSelect[0].value,
combinedColumnName: $.trim(elmts.combinedColumnNameInput[0].value),
prependColumnName: elmts.prependColumnNameCheckbox[0].checked,
separator: elmts.separatorInput[0].value,
ignoreBlankCells: elmts.ignoreBlankCellsCheckbox[0].checked
};
Gridworks.postProcess(
"transpose-columns-into-rows",
config,
null,
{ modelsChanged: true }
);
dismiss();
});
for (var i = 0; i < columns.length; i++) {
var column = columns[i];
var option = $('<option>').attr("value", column.name).text(column.name).appendTo(elmts.fromColumnSelect);
if (column.name == this._column.name) {
option.attr("selected", "true");
}
}
var populateToColumn = function() {
elmts.toColumnSelect.empty();
var toColumnName = elmts.fromColumnSelect[0].value;
var j = 0;
for (; j < columns.length; j++) {
var column = columns[j];
if (column.name == toColumnName) {
break;
}
}
for (var k = j + 1; k < columns.length; k++) {
var column = columns[k];
var option = $('<option>').attr("value", k - j + 1).text(column.name).appendTo(elmts.toColumnSelect);
if (k == columns.length - 1) {
option.attr("selected", "true");
}
}
};
populateToColumn();
elmts.fromColumnSelect.bind("change", populateToColumn);
};
DataTableColumnHeaderUI.prototype._showSortingCriterion = function(criterion, hasOtherCriteria) { DataTableColumnHeaderUI.prototype._showSortingCriterion = function(criterion, hasOtherCriteria) {
var self = this; var self = this;

View File

@ -0,0 +1,38 @@
<div class="dialog-frame" style="width: 600px;">
<div class="dialog-header" bind="dialogHeader"></div>
<div class="dialog-body" bind="dialogBody">
<div class="grid-layout layout-normal layout-full grid-layout-for-ui"><table>
<tr>
<td>From Column</td>
<td>To Column</td>
<td>Formatting Options</td>
</tr>
<tr>
<td><select bind="fromColumnSelect" size="20" style="width: 100%;"></select></td>
<td><select bind="toColumnSelect" size="20" style="width: 100%;"></select></td>
<td><div class="grid-layout layout-tightest"><table>
<tr>
<td colspan="2">Combined column name <input bind="combinedColumnNameInput" size="15" /></td>
</tr>
<tr>
<td colspan="2"><input type="checkbox" bind="prependColumnNameCheckbox" checked /> prepend column name</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>Separate column name and cell value with
<input bind="separatorInput" size="5" value=":" />
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox" bind="ignoreBlankCellsCheckbox" checked /> ignore blank cells</td>
</tr>
</table></div></td>
</tr>
</table></div>
</div>
<div class="dialog-footer" bind="dialogFooter">
<button bind="okButton">Transpose</button>
<button bind="cancelButton">Cancel</button>
</div>
</div>