Made operation "Transpose columns into rows" support the option of transposing into 2 new columns rather than just one.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@2362 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2011-11-06 02:50:33 +00:00
parent 85a37d23f9
commit a35b9f53f7
4 changed files with 234 additions and 84 deletions

View File

@ -53,17 +53,29 @@ public class TransposeColumnsIntoRowsCommand extends Command {
try {
Project project = getProject(request);
AbstractOperation op;
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);
String combinedColumnName = request.getParameter("combinedColumnName");
if (combinedColumnName != null) {
boolean prependColumnName = Boolean.parseBoolean(request.getParameter("prependColumnName"));
String separator = request.getParameter("separator");
op = new TransposeColumnsIntoRowsOperation(
startColumnName, columnCount,
combinedColumnName, prependColumnName, separator,
ignoreBlankCells);
} else {
String keyColumnName = request.getParameter("keyColumnName");
String valueColumnName = request.getParameter("valueColumnName");
op = new TransposeColumnsIntoRowsOperation(
startColumnName, columnCount,
keyColumnName, valueColumnName,
ignoreBlankCells);
}
Process process = op.createProcess(project, new Properties());

View File

@ -49,24 +49,40 @@ import com.google.refine.model.Project;
import com.google.refine.model.Row;
import com.google.refine.model.changes.MassRowColumnChange;
import com.google.refine.operations.OperationRegistry;
import com.google.refine.util.JSONUtilities;
public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
final protected String _startColumnName;
final protected int _columnCount;
final protected boolean _ignoreBlankCells;
final protected String _combinedColumnName;
final protected boolean _prependColumnName;
final protected String _separator;
final protected boolean _ignoreBlankCells;
final protected String _keyColumnName;
final protected String _valueColumnName;
static public AbstractOperation reconstruct(Project project, JSONObject obj) throws Exception {
String combinedColumnName = JSONUtilities.getString(obj, "combinedColumnName", null);
if (combinedColumnName != null) {
return new TransposeColumnsIntoRowsOperation(
obj.getString("startColumnName"),
obj.getInt("columnCount"),
obj.getString("combinedColumnName"),
combinedColumnName,
obj.getBoolean("prependColumnName"),
obj.getString("separator"),
obj.getBoolean("ignoreBlankCells")
);
} else {
return new TransposeColumnsIntoRowsOperation(
obj.getString("startColumnName"),
obj.getInt("columnCount"),
obj.getString("keyColumnName"),
obj.getString("valueColumnName"),
obj.getBoolean("ignoreBlankCells")
);
}
}
public TransposeColumnsIntoRowsOperation(
@ -79,10 +95,33 @@ public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
) {
_startColumnName = startColumnName;
_columnCount = columnCount;
_ignoreBlankCells = ignoreBlankCells;
_combinedColumnName = combinedColumnName;
_prependColumnName = prependColumnName;
_separator = separator;
_keyColumnName = null;
_valueColumnName = null;
}
public TransposeColumnsIntoRowsOperation(
String startColumnName,
int columnCount,
String keyColumnName,
String valueColumnName,
boolean ignoreBlankCells
) {
_startColumnName = startColumnName;
_columnCount = columnCount;
_ignoreBlankCells = ignoreBlankCells;
_combinedColumnName = null;
_prependColumnName = false;
_separator = null;
_keyColumnName = keyColumnName;
_valueColumnName = valueColumnName;
}
@Override
@ -91,37 +130,66 @@ public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
writer.object();
writer.key("op"); writer.value(OperationRegistry.s_opClassToName.get(this.getClass()));
writer.key("description"); writer.value(
_columnCount > 0 ?
("Transpose cells in " + _columnCount +
" column(s) starting with " + _startColumnName + " into rows") :
("Transpose cells in columns starting with " +
_startColumnName + " into rows"));
writer.key("description"); writer.value(getBriefDescription());
writer.key("startColumnName"); writer.value(_startColumnName);
writer.key("columnCount"); writer.value(_columnCount);
writer.key("ignoreBlankCells"); writer.value(_ignoreBlankCells);
if (_combinedColumnName != null) {
writer.key("combinedColumnName"); writer.value(_combinedColumnName);
writer.key("prependColumnName"); writer.value(_prependColumnName);
writer.key("separator"); writer.value(_separator);
writer.key("ignoreBlankCells"); writer.value(_ignoreBlankCells);
} else {
writer.key("keyColumnName"); writer.value(_keyColumnName);
writer.key("valueColumnName"); writer.value(_valueColumnName);
}
writer.endObject();
}
@Override
protected String getBriefDescription(Project project) {
return _columnCount > 0 ?
("Transpose cells in " + _columnCount +
" column(s) starting with " + _startColumnName + " into rows") :
("Transpose cells in columns starting with " +
_startColumnName + " into rows");
return getBriefDescription();
}
protected String getBriefDescription() {
if (_combinedColumnName != null) {
if (_columnCount > 0) {
return "Transpose cells in " + _columnCount +
" column(s) starting with " + _startColumnName +
" into rows in one new column named " + _combinedColumnName;
} else {
return "Transpose cells in columns starting with " +
_startColumnName +
" into rows in one new column named " + _combinedColumnName;
}
} else {
if (_columnCount > 0) {
return "Transpose cells in " + _columnCount +
" column(s) starting with " + _startColumnName +
" into rows in two new columns named " +
_keyColumnName + " and " + _valueColumnName;
} else {
return "Transpose cells in columns starting with " +
_startColumnName +
" into rows in two new columns named " +
_keyColumnName + " and " + _valueColumnName;
}
}
}
@Override
protected HistoryEntry createHistoryEntry(Project project, long historyEntryID) throws Exception {
if (_combinedColumnName != null &&
!_combinedColumnName.isEmpty() &&
project.columnModel.getColumnByName(_combinedColumnName) != null) {
if (_combinedColumnName != null) {
if (project.columnModel.getColumnByName(_combinedColumnName) != null) {
throw new Exception("Another column already named " + _combinedColumnName);
}
} else {
if (project.columnModel.getColumnByName(_keyColumnName) != null) {
throw new Exception("Another column already named " + _keyColumnName);
}
if (project.columnModel.getColumnByName(_valueColumnName) != null) {
throw new Exception("Another column already named " + _valueColumnName);
}
}
List<Column> newColumns = new ArrayList<Column>();
List<Column> oldColumns = project.columnModel.columns;
@ -149,10 +217,12 @@ public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
startColumnIndex = c;
String columnName = _combinedColumnName != null && _combinedColumnName.length() > 0 ? _combinedColumnName : column.getName();
Column newColumn = new Column(newColumns.size(), columnName);
newColumns.add(newColumn);
if (_combinedColumnName != null) {
newColumns.add(new Column(newColumns.size(), _combinedColumnName));
} else {
newColumns.add(new Column(newColumns.size(), _keyColumnName));
newColumns.add(new Column(newColumns.size(), _valueColumnName));
}
columnsLeftToTranspose--;
} else {
@ -172,11 +242,12 @@ public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
startColumnIndex = c;
String columnName = _combinedColumnName != null && _combinedColumnName.length() > 0 ?
_combinedColumnName : column.getName();
Column newColumn = new Column(newColumns.size(), columnName);
newColumns.add(newColumn);
if (_combinedColumnName != null) {
newColumns.add(new Column(newColumns.size(), _combinedColumnName));
} else {
newColumns.add(new Column(newColumns.size(), _keyColumnName));
newColumns.add(new Column(newColumns.size(), _valueColumnName));
}
break;
} else {
// This column is before all columns to transpose
@ -206,8 +277,8 @@ public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
if (c < startColumnIndex) {
firstNewRow.setCell(c, cell);
} else if (c == startColumnIndex || c < startColumnIndex + columnCount) {
if (_combinedColumnName != null) {
Cell newCell;
if (cell == null || cell.value == null) {
if (_prependColumnName && !_ignoreBlankCells) {
newCell = new Cell(column.getName() + _separator, null);
@ -220,18 +291,38 @@ public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
newCell = cell;
}
Row rowToModify;
if (transposedCells == 0) {
firstNewRow.setCell(startColumnIndex, newCell);
rowToModify = firstNewRow;
} else {
Row newRow = new Row(newColumns.size());
newRow.setCell(startColumnIndex, newCell);
newRows.add(newRow);
rowToModify = new Row(newColumns.size());
newRows.add(rowToModify);
}
rowToModify.setCell(startColumnIndex, newCell);
transposedCells++;
} else {
firstNewRow.setCell(c - columnCount + 1, cell);
if (_ignoreBlankCells && (cell == null || cell.value == null)) {
continue;
}
Row rowToModify;
if (transposedCells == 0) {
rowToModify = firstNewRow;
} else {
rowToModify = new Row(newColumns.size());
newRows.add(rowToModify);
}
rowToModify.setCell(startColumnIndex, new Cell(column.getName(), null));
rowToModify.setCell(startColumnIndex + 1, cell);
transposedCells++;
}
} else {
firstNewRow.setCell(
c - columnCount + (_combinedColumnName != null ? 1 : 2),
cell);
}
}
}
@ -239,7 +330,7 @@ public class TransposeColumnsIntoRowsOperation extends AbstractOperation {
return new HistoryEntry(
historyEntryID,
project,
getBriefDescription(null),
getBriefDescription(),
this,
new MassRowColumnChange(newColumns, newRows)
);

View File

@ -253,19 +253,42 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) {
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
};
var mode = dialog.find('input[name="transpose-dialog-column-choices"]:checked')[0].value;
if (mode == "2") {
config.keyColumnName = $.trim(elmts.keyColumnNameInput[0].value);
config.valueColumnName = $.trim(elmts.valueColumnNameInput[0].value);
if (config.keyColumnName == "") {
alert("Please specify the new key column's name.");
return;
} else if (config.valueColumnName == "") {
alert("Please specify the new value column's name.");
return;
}
} else {
config.combinedColumnName = $.trim(elmts.combinedColumnNameInput[0].value);
config.prependColumnName = elmts.prependColumnNameCheckbox[0].checked;
config.separator = elmts.separatorInput[0].value;
if (config.combinedColumnName == "") {
alert("Please specify the new column's name.");
return;
} else if (config.prependColumnName && config.separator == "") {
alert("Please specify the separator between original column names and cell values.");
return;
}
}
Refine.postCoreProcess(
"transpose-columns-into-rows",
config,
null,
{ modelsChanged: true }
{ modelsChanged: true },
{
onDone: dismiss
}
);
dismiss();
});
for (var i = 0; i < columns.length; i++) {

View File

@ -1,4 +1,4 @@
<div class="dialog-frame" style="width: 600px;">
<div class="dialog-frame" style="width: 700px;">
<div class="dialog-border">
<div class="dialog-header" bind="dialogHeader">Transpose Cells Across Columns into Rows</div>
<div class="dialog-body" bind="dialogBody">
@ -6,29 +6,53 @@
<tr>
<td>From Column</td>
<td>To Column</td>
<td>Formatting Options</td>
<td>Transpose into</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-tight"><table>
<tr>
<td><input type="radio" id="$transpose-dialog-two-columns" name="transpose-dialog-column-choices" value="2" checked /></td>
<td><label for="$transpose-dialog-two-columns">Two new columns</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><div class="grid-layout layout-tightest"><table>
<tr>
<td colspan="2">Combined column name <input bind="combinedColumnNameInput" size="15" /></td>
<td>Key column</td>
<td><input bind="keyColumnNameInput" size="15"> (containing original columns' names)</td>
</tr>
<tr>
<td colspan="2"><input type="checkbox" bind="prependColumnNameCheckbox" checked id="$transpose-dialog-prepend" />
<label for="$transpose-dialog-prepend">prepend column name</label></td>
<td>Value column</td>
<td><input bind="valueColumnNameInput" size="15"> (containing original cells' values)</td>
</tr>
</table></div></td>
</tr>
<tr>
<td><input type="radio" id="$transpose-dialog-one-column" name="transpose-dialog-column-choices" value="1" /></td>
<td><label for="$transpose-dialog-one-column">One column <input bind="combinedColumnNameInput" size="15" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td>Separate column name and cell value with
<input bind="separatorInput" size="5" value=":" />
<td><div class="grid-layout layout-tightest"><table>
<tr>
<td><input type="checkbox" bind="prependColumnNameCheckbox" id="$transpose-dialog-prepend" /></td>
<td><label for="$transpose-dialog-prepend">prepend the original column's name to each cell</label></td>
</tr>
</tr>
<tr>
<td>&nbsp;</td>
<td>followed by
<input bind="separatorInput" size="2" value=":" />
before the cell's value
</td>
</tr>
</table></div></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox" bind="ignoreBlankCellsCheckbox" checked id="$transpose-dialog-ignore" />
<label for="$transpose-dialog-ignore">ignore blank cells</label></td>
<td><input type="checkbox" bind="ignoreBlankCellsCheckbox" checked id="$transpose-dialog-ignore" /></td>
<td><label for="$transpose-dialog-ignore">Ignore blank cells</label></td>
</tr>
</table></div></td>
</tr>