Implemented "judge one cell" command for making recon judgment per cell.
git-svn-id: http://google-refine.googlecode.com/svn/trunk@80 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
66457f0515
commit
97e2e0eddc
@ -33,6 +33,7 @@ import com.metaweb.gridworks.commands.info.GetRowsCommand;
|
|||||||
import com.metaweb.gridworks.commands.recon.ApproveNewReconcileCommand;
|
import com.metaweb.gridworks.commands.recon.ApproveNewReconcileCommand;
|
||||||
import com.metaweb.gridworks.commands.recon.ApproveReconcileCommand;
|
import com.metaweb.gridworks.commands.recon.ApproveReconcileCommand;
|
||||||
import com.metaweb.gridworks.commands.recon.DiscardReconcileCommand;
|
import com.metaweb.gridworks.commands.recon.DiscardReconcileCommand;
|
||||||
|
import com.metaweb.gridworks.commands.recon.JudgeOneCellCommand;
|
||||||
import com.metaweb.gridworks.commands.recon.ReconcileCommand;
|
import com.metaweb.gridworks.commands.recon.ReconcileCommand;
|
||||||
import com.metaweb.gridworks.commands.util.GetExpressionLanguageInfoCommand;
|
import com.metaweb.gridworks.commands.util.GetExpressionLanguageInfoCommand;
|
||||||
import com.metaweb.gridworks.commands.util.PreviewExpressionCommand;
|
import com.metaweb.gridworks.commands.util.PreviewExpressionCommand;
|
||||||
@ -67,6 +68,7 @@ public class GridworksServlet extends HttpServlet {
|
|||||||
_commands.put("approve-reconcile", new ApproveReconcileCommand());
|
_commands.put("approve-reconcile", new ApproveReconcileCommand());
|
||||||
_commands.put("approve-new-reconcile", new ApproveNewReconcileCommand());
|
_commands.put("approve-new-reconcile", new ApproveNewReconcileCommand());
|
||||||
_commands.put("discard-reconcile", new DiscardReconcileCommand());
|
_commands.put("discard-reconcile", new DiscardReconcileCommand());
|
||||||
|
_commands.put("judge-one-cell", new JudgeOneCellCommand());
|
||||||
|
|
||||||
_commands.put("preview-expression", new PreviewExpressionCommand());
|
_commands.put("preview-expression", new PreviewExpressionCommand());
|
||||||
_commands.put("get-expression-language-info", new GetExpressionLanguageInfoCommand());
|
_commands.put("get-expression-language-info", new GetExpressionLanguageInfoCommand());
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
package com.metaweb.gridworks.commands.recon;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.json.JSONWriter;
|
||||||
|
|
||||||
|
import com.metaweb.gridworks.commands.Command;
|
||||||
|
import com.metaweb.gridworks.history.Change;
|
||||||
|
import com.metaweb.gridworks.history.HistoryEntry;
|
||||||
|
import com.metaweb.gridworks.model.Cell;
|
||||||
|
import com.metaweb.gridworks.model.Column;
|
||||||
|
import com.metaweb.gridworks.model.Project;
|
||||||
|
import com.metaweb.gridworks.model.Recon;
|
||||||
|
import com.metaweb.gridworks.model.ReconCandidate;
|
||||||
|
import com.metaweb.gridworks.model.changes.CellChange;
|
||||||
|
import com.metaweb.gridworks.process.Process;
|
||||||
|
import com.metaweb.gridworks.process.QuickHistoryEntryProcess;
|
||||||
|
|
||||||
|
public class JudgeOneCellCommand extends Command {
|
||||||
|
@Override
|
||||||
|
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Project project = getProject(request);
|
||||||
|
|
||||||
|
int rowIndex = Integer.parseInt(request.getParameter("row"));
|
||||||
|
int cellIndex = Integer.parseInt(request.getParameter("cell"));
|
||||||
|
Cell cell = project.rows.get(rowIndex).getCell(cellIndex);
|
||||||
|
if (cell == null || cell.value == null) {
|
||||||
|
respond(response, "{ \"code\" : \"error\", \"message\" : \"Cell is blank\" }");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Column column = project.columnModel.getColumnByCellIndex(cellIndex);
|
||||||
|
if (column == null) {
|
||||||
|
respond(response, "{ \"code\" : \"error\", \"message\" : \"No such column\" }");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cell newCell = new Cell(
|
||||||
|
cell.value,
|
||||||
|
cell.recon == null ? new Recon() : cell.recon.dup()
|
||||||
|
);
|
||||||
|
|
||||||
|
String cellDescription =
|
||||||
|
"single cell on row " + (rowIndex + 1) +
|
||||||
|
", column " + column.getHeaderLabel() +
|
||||||
|
", containing \"" + cell.value + "\"";
|
||||||
|
|
||||||
|
String description = null;
|
||||||
|
|
||||||
|
String judgment = request.getParameter("judgment");
|
||||||
|
if ("match".equals(judgment)) {
|
||||||
|
ReconCandidate match = null;
|
||||||
|
|
||||||
|
if (cell.recon != null) {
|
||||||
|
String candidateID = request.getParameter("candidate");
|
||||||
|
|
||||||
|
for (ReconCandidate c : cell.recon.candidates) {
|
||||||
|
if (candidateID.equals(c.topicID)) {
|
||||||
|
match = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match == null) {
|
||||||
|
respond(response, "{ \"code\" : \"error\", \"message\" : \"No such recon candidate\" }");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newCell.recon.judgment = Recon.Judgment.Matched;
|
||||||
|
newCell.recon.match = match;
|
||||||
|
|
||||||
|
description = "Match " + match.topicName +
|
||||||
|
" (" + match.topicID + ") to " +
|
||||||
|
cellDescription;
|
||||||
|
|
||||||
|
} else if ("new".equals(judgment)) {
|
||||||
|
newCell.recon.judgment = Recon.Judgment.New;
|
||||||
|
description = "Mark to create new topic for " + cellDescription;
|
||||||
|
} else if ("discard".equals(judgment)) {
|
||||||
|
newCell.recon.judgment = Recon.Judgment.None;
|
||||||
|
newCell.recon.match = null;
|
||||||
|
description = "Discard recon judgment for " + cellDescription;
|
||||||
|
} else {
|
||||||
|
respond(response, "{ \"code\" : \"error\", \"message\" : \"bad judgment\" }");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Change change = new CellChange(rowIndex, cellIndex, cell, newCell);
|
||||||
|
HistoryEntry historyEntry = new HistoryEntry(
|
||||||
|
project, description, null, change);
|
||||||
|
|
||||||
|
Process process = new QuickHistoryEntryProcess(project, historyEntry);
|
||||||
|
|
||||||
|
boolean done = project.processManager.queueProcess(process);
|
||||||
|
if (done) {
|
||||||
|
JSONWriter writer = new JSONWriter(response.getWriter());
|
||||||
|
writer.object();
|
||||||
|
writer.key("code"); writer.value("ok");
|
||||||
|
writer.key("cell"); newCell.write(writer, new Properties());
|
||||||
|
writer.endObject();
|
||||||
|
} else {
|
||||||
|
respond(response, "{ \"code\" : \"pending\" }");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
respondException(response, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ public class Recon implements Serializable, HasFields, Jsonizable {
|
|||||||
|
|
||||||
static public enum Judgment {
|
static public enum Judgment {
|
||||||
None,
|
None,
|
||||||
Approve,
|
Matched,
|
||||||
New
|
New
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +41,8 @@ public class Recon implements Serializable, HasFields, Jsonizable {
|
|||||||
return candidates.size() > 0 ? candidates.get(0) : null;
|
return candidates.size() > 0 ? candidates.get(0) : null;
|
||||||
} else if ("judgment".equals(name) || "judgement".equals(name)) {
|
} else if ("judgment".equals(name) || "judgement".equals(name)) {
|
||||||
return judgmentToString();
|
return judgmentToString();
|
||||||
} else if ("approved".equals(name)) {
|
} else if ("matched".equals(name)) {
|
||||||
return judgment == Judgment.Approve;
|
return judgment == Judgment.Matched;
|
||||||
} else if ("new".equals(name)) {
|
} else if ("new".equals(name)) {
|
||||||
return judgment == Judgment.New;
|
return judgment == Judgment.New;
|
||||||
} else if ("match".equals(name)) {
|
} else if ("match".equals(name)) {
|
||||||
@ -58,8 +58,8 @@ public class Recon implements Serializable, HasFields, Jsonizable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String judgmentToString() {
|
protected String judgmentToString() {
|
||||||
if (judgment == Judgment.Approve) {
|
if (judgment == Judgment.Matched) {
|
||||||
return "approve";
|
return "matched";
|
||||||
} else if (judgment == Judgment.New) {
|
} else if (judgment == Judgment.New) {
|
||||||
return "new";
|
return "new";
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,21 +1,29 @@
|
|||||||
package com.metaweb.gridworks.model.changes;
|
package com.metaweb.gridworks.model.changes;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import com.metaweb.gridworks.history.Change;
|
||||||
|
|
||||||
import com.metaweb.gridworks.model.Cell;
|
import com.metaweb.gridworks.model.Cell;
|
||||||
|
import com.metaweb.gridworks.model.Project;
|
||||||
|
|
||||||
public class CellChange implements Serializable {
|
public class CellChange implements Change {
|
||||||
private static final long serialVersionUID = -2637405780084390883L;
|
private static final long serialVersionUID = -2637405780084390883L;
|
||||||
|
|
||||||
final public int row;
|
final public int row;
|
||||||
final public int column;
|
final public int cellIndex;
|
||||||
final public Cell oldCell;
|
final public Cell oldCell;
|
||||||
final public Cell newCell;
|
final public Cell newCell;
|
||||||
|
|
||||||
public CellChange(int row, int column, Cell oldCell, Cell newCell) {
|
public CellChange(int row, int column, Cell oldCell, Cell newCell) {
|
||||||
this.row = row;
|
this.row = row;
|
||||||
this.column = column;
|
this.cellIndex = column;
|
||||||
this.oldCell = oldCell;
|
this.oldCell = oldCell;
|
||||||
this.newCell = newCell;
|
this.newCell = newCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.rows.get(row).setCell(cellIndex, newCell);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void revert(Project project) {
|
||||||
|
project.rows.get(row).setCell(cellIndex, oldCell);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ public class MassCellChange implements Change {
|
|||||||
List<Row> rows = project.rows;
|
List<Row> rows = project.rows;
|
||||||
|
|
||||||
for (CellChange cellChange : _cellChanges) {
|
for (CellChange cellChange : _cellChanges) {
|
||||||
rows.get(cellChange.row).cells.set(cellChange.column, cellChange.newCell);
|
rows.get(cellChange.row).setCell(cellChange.cellIndex, cellChange.newCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_commonCellIndex >= 0) {
|
if (_commonCellIndex >= 0) {
|
||||||
@ -44,7 +44,7 @@ public class MassCellChange implements Change {
|
|||||||
List<Row> rows = project.rows;
|
List<Row> rows = project.rows;
|
||||||
|
|
||||||
for (CellChange cellChange : _cellChanges) {
|
for (CellChange cellChange : _cellChanges) {
|
||||||
rows.get(cellChange.row).cells.set(cellChange.column, cellChange.oldCell);
|
rows.get(cellChange.row).setCell(cellChange.cellIndex, cellChange.oldCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_commonCellIndex >= 0) {
|
if (_commonCellIndex >= 0) {
|
||||||
|
@ -56,7 +56,7 @@ public class ApproveReconOperation extends EngineDependentMassCellOperation {
|
|||||||
cell.recon.dup()
|
cell.recon.dup()
|
||||||
);
|
);
|
||||||
newCell.recon.match = newCell.recon.candidates.get(0);
|
newCell.recon.match = newCell.recon.candidates.get(0);
|
||||||
newCell.recon.judgment = Judgment.Approve;
|
newCell.recon.judgment = Judgment.Matched;
|
||||||
|
|
||||||
CellChange cellChange = new CellChange(rowIndex, cellIndex, cell, newCell);
|
CellChange cellChange = new CellChange(rowIndex, cellIndex, cell, newCell);
|
||||||
cellChanges.add(cellChange);
|
cellChanges.add(cellChange);
|
||||||
|
@ -9,10 +9,14 @@ function DataTableCellUI(dataTableView, cell, rowIndex, cellIndex, td) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
DataTableCellUI.prototype._render = function() {
|
DataTableCellUI.prototype._render = function() {
|
||||||
|
var self = this;
|
||||||
var cell = this._cell;
|
var cell = this._cell;
|
||||||
var td = this._td;
|
var td = this._td;
|
||||||
|
|
||||||
|
$(td).empty();
|
||||||
|
|
||||||
if (cell == null || cell.v == null) {
|
if (cell == null || cell.v == null) {
|
||||||
|
// TODO: content editing UI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,31 +25,114 @@ DataTableCellUI.prototype._render = function() {
|
|||||||
} else {
|
} else {
|
||||||
var r = cell.r;
|
var r = cell.r;
|
||||||
if (r.j == "new") {
|
if (r.j == "new") {
|
||||||
$(td).html(cell.v + " (new)");
|
$(td).html(cell.v + " (new topic)");
|
||||||
} else if (r.j == "approve" && "m" in r && r.m != null) {
|
|
||||||
|
$('<span> </span>').appendTo(td);
|
||||||
|
$('<a href="javascript:{}">re-match</a>')
|
||||||
|
.addClass("data-table-recon-action")
|
||||||
|
.appendTo(td).click(function(evt) {
|
||||||
|
self._doRematch();
|
||||||
|
});
|
||||||
|
} else if (r.j == "matched" && "m" in r && r.m != null) {
|
||||||
var match = cell.r.m;
|
var match = cell.r.m;
|
||||||
$('<a></a>')
|
$('<a></a>')
|
||||||
.attr("href", "http://www.freebase.com/view" + match.id)
|
.attr("href", "http://www.freebase.com/view" + match.id)
|
||||||
.attr("target", "_blank")
|
.attr("target", "_blank")
|
||||||
.text(match.name)
|
.text(match.name)
|
||||||
.appendTo(td);
|
.appendTo(td);
|
||||||
|
|
||||||
|
$('<span> </span>').appendTo(td);
|
||||||
|
$('<a href="javascript:{}">re-match</a>')
|
||||||
|
.addClass("data-table-recon-action")
|
||||||
|
.appendTo(td).click(function(evt) {
|
||||||
|
self._doRematch();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
$(td).html(cell.v);
|
$(td).html(cell.v);
|
||||||
|
$('<span> </span>').appendTo(td);
|
||||||
|
$('<a href="javascript:{}">mark as new</a>')
|
||||||
|
.addClass("data-table-recon-action")
|
||||||
|
.appendTo(td).click(function(evt) {
|
||||||
|
self._doMarkAsNew();
|
||||||
|
});
|
||||||
|
|
||||||
if (this._dataTableView._showRecon && "c" in r && r.c.length > 0) {
|
if (this._dataTableView._showRecon && "c" in r && r.c.length > 0) {
|
||||||
var candidates = r.c;
|
var candidates = r.c;
|
||||||
var ul = $('<ul></ul>').appendTo(td);
|
var ul = $('<ul></ul>').addClass("data-table-recon-candidates").appendTo(td);
|
||||||
|
var renderCandidate = function(candidate, index) {
|
||||||
for (var i = 0; i < candidates.length; i++) {
|
|
||||||
var candidate = candidates[i];
|
|
||||||
var li = $('<li></li>').appendTo(ul);
|
var li = $('<li></li>').appendTo(ul);
|
||||||
$('<a></a>')
|
$('<a></a>')
|
||||||
|
.addClass("data-table-recon-topic")
|
||||||
.attr("href", "http://www.freebase.com/view" + candidate.id)
|
.attr("href", "http://www.freebase.com/view" + candidate.id)
|
||||||
.attr("target", "_blank")
|
.attr("target", "_blank")
|
||||||
.text(candidate.name)
|
.text(candidate.name)
|
||||||
.appendTo(li);
|
.appendTo(li);
|
||||||
$('<span></span>').addClass("recon-score").text("(" + Math.round(candidate.score) + ")").appendTo(li);
|
|
||||||
|
$('<span></span>').addClass("data-table-recon-score").text("(" + Math.round(candidate.score) + ")").appendTo(li);
|
||||||
|
$('<a href="javascript:{}">match</a>')
|
||||||
|
.addClass("data-table-recon-action")
|
||||||
|
.appendTo(li).click(function(evt) {
|
||||||
|
self._doSetAsMatch(candidate.id);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < candidates.length; i++) {
|
||||||
|
renderCandidate(candidates[i], i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DataTableCellUI.prototype._doRematch = function() {
|
||||||
|
this._doJudgment("discard");
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTableCellUI.prototype._doMarkAsNew = function() {
|
||||||
|
this._doJudgment("new");
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTableCellUI.prototype._doSetAsMatch = function(candidateID) {
|
||||||
|
this._doJudgment("match", { candidate : candidateID });
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTableCellUI.prototype._doJudgment = function(judgment, params) {
|
||||||
|
params = params || {};
|
||||||
|
params.row = this._rowIndex;
|
||||||
|
params.cell = this._cellIndex;
|
||||||
|
params.judgment = judgment;
|
||||||
|
this.doPostThenUpdate("judge-one-cell", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTableCellUI.prototype.createUpdateFunction = function(onBefore) {
|
||||||
|
var self = this;
|
||||||
|
return function(data) {
|
||||||
|
if (data.code == "ok") {
|
||||||
|
var onDone = function() {
|
||||||
|
self._cell = data.cell;
|
||||||
|
self._render();
|
||||||
|
ui.historyWidget.update();
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var onDone = function() {
|
||||||
|
ui.processWidget.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onBefore) {
|
||||||
|
onBefore(onDone);
|
||||||
|
} else {
|
||||||
|
onDone();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTableCellUI.prototype.doPostThenUpdate = function(command, params) {
|
||||||
|
params.project = theProject.id;
|
||||||
|
$.post(
|
||||||
|
"/command/" + command + "?" + $.param(params),
|
||||||
|
null,
|
||||||
|
this.createUpdateFunction(),
|
||||||
|
"json"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -94,8 +94,3 @@ a.facet-choice-link:hover {
|
|||||||
.facet-text-body input {
|
.facet-text-body input {
|
||||||
width: 98%;
|
width: 98%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recon-score {
|
|
||||||
color: #aaa;
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
@ -41,3 +41,35 @@ img.column-header-menu {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ul.data-table-recon-candidates {
|
||||||
|
margin: 0.75em 1.5em;
|
||||||
|
padding: 0;
|
||||||
|
list-style: circle;
|
||||||
|
color: #88a;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.data-table-recon-topic {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #88a;
|
||||||
|
}
|
||||||
|
a.data-table-recon-topic:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #008;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table-recon-score {
|
||||||
|
color: #aaa;
|
||||||
|
margin: 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.data-table-recon-action {
|
||||||
|
font-size: 80%;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #aaf;
|
||||||
|
}
|
||||||
|
a.data-table-recon-action:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #008;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user