Track and display recon stats in column headers.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@146 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-02-26 23:33:16 +00:00
parent aadf98a388
commit f5ff9044cf
17 changed files with 302 additions and 62 deletions

View File

@ -18,8 +18,10 @@ 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.ReconStats;
import com.metaweb.gridworks.model.Recon.Judgment;
import com.metaweb.gridworks.model.changes.CellChange;
import com.metaweb.gridworks.model.changes.ReconChange;
import com.metaweb.gridworks.process.QuickHistoryEntryProcess;
public class ReconJudgeOneCellCommand extends Command {
@ -107,6 +109,8 @@ public class ReconJudgeOneCellCommand extends Command {
throw new Exception("No such column");
}
Judgment oldJudgment = cell.recon == null ? Judgment.None : cell.recon.judgment;
newCell = new Cell(
cell.value,
cell.recon == null ? new Recon() : cell.recon.dup()
@ -134,8 +138,39 @@ public class ReconJudgeOneCellCommand extends Command {
cellDescription;
}
Change change = new CellChange(rowIndex, cellIndex, cell, newCell);
ReconStats stats = column.getReconStats();
if (stats == null) {
stats = ReconStats.create(_project, cellIndex);
} else {
int newChange = 0;
int matchChange = 0;
if (oldJudgment == Judgment.New) {
newChange--;
}
if (oldJudgment == Judgment.Matched) {
matchChange--;
}
if (newCell.recon.judgment == Judgment.New) {
newChange++;
}
if (newCell.recon.judgment == Judgment.Matched) {
matchChange++;
}
stats = new ReconStats(
stats.nonBlanks,
stats.newTopics + newChange,
stats.matchedTopics + matchChange);
}
Change change = new ReconChange(
new CellChange(rowIndex, cellIndex, cell, newCell),
column.getHeaderLabel(),
column.getReconConfig(),
stats
);
return new HistoryEntry(
_project, description, null, change);
}

View File

@ -17,6 +17,7 @@ public class Column implements Serializable, Jsonizable {
final private String _originalHeaderLabel;
private String _headerLabel;
private ReconConfig _reconConfig;
private ReconStats _reconStats;
transient protected Map<String, Object> _precomputes;
@ -49,6 +50,14 @@ public class Column implements Serializable, Jsonizable {
return _reconConfig;
}
public void setReconStats(ReconStats stats) {
this._reconStats = stats;
}
public ReconStats getReconStats() {
return _reconStats;
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
@ -59,6 +68,10 @@ public class Column implements Serializable, Jsonizable {
writer.key("reconConfig");
_reconConfig.write(writer, options);
}
if (_reconStats != null) {
writer.key("reconStats");
_reconStats.write(writer, options);
}
writer.endObject();
}

View File

@ -1,6 +1,6 @@
package com.metaweb.gridworks.model;
import java.io.Serializable;
import java.io.Serializable;
import java.util.Properties;
import org.json.JSONException;

View File

@ -0,0 +1,58 @@
package com.metaweb.gridworks.model;
import java.io.Serializable;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.Jsonizable;
import com.metaweb.gridworks.expr.ExpressionUtils;
import com.metaweb.gridworks.model.Recon.Judgment;
public class ReconStats implements Serializable, Jsonizable {
private static final long serialVersionUID = -4831409797104437854L;
final public int nonBlanks;
final public int newTopics;
final public int matchedTopics;
public ReconStats(int nonBlanks, int newTopics, int matchedTopics) {
this.nonBlanks = nonBlanks;
this.newTopics = newTopics;
this.matchedTopics = matchedTopics;
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("nonBlanks"); writer.value(nonBlanks);
writer.key("newTopics"); writer.value(newTopics);
writer.key("matchedTopics"); writer.value(matchedTopics);
writer.endObject();
}
static public ReconStats create(Project project, int cellIndex) {
int nonBlanks = 0;
int newTopics = 0;
int matchedTopics = 0;
for (Row row : project.rows) {
Cell cell = row.getCell(cellIndex);
if (cell != null && !ExpressionUtils.isBlank(cell.value)) {
nonBlanks++;
if (cell.recon != null) {
if (cell.recon.judgment == Judgment.New) {
newTopics++;
} else if (cell.recon.judgment == Judgment.Matched) {
matchedTopics++;
}
}
}
}
return new ReconStats(nonBlanks, newTopics, matchedTopics);
}
}

View File

@ -22,6 +22,15 @@ public class MassCellChange implements Change {
_updateRowContextDependencies = updateRowContextDependencies;
}
public MassCellChange(CellChange cellChange, String commonColumnName, boolean updateRowContextDependencies) {
_cellChanges = new CellChange[1];
_cellChanges[0] = cellChange;
_commonColumnName = commonColumnName;
_updateRowContextDependencies = updateRowContextDependencies;
}
public void apply(Project project) {
synchronized (project) {
List<Row> rows = project.rows;

View File

@ -0,0 +1,73 @@
/**
*
*/
package com.metaweb.gridworks.model.changes;
import java.util.List;
import com.metaweb.gridworks.model.Column;
import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.ReconConfig;
import com.metaweb.gridworks.model.ReconStats;
public class ReconChange extends MassCellChange {
private static final long serialVersionUID = 7048806528587330543L;
final protected ReconConfig _newReconConfig;
protected ReconStats _newReconStats;
protected ReconConfig _oldReconConfig;
protected ReconStats _oldReconStats;
public ReconChange(
List<CellChange> cellChanges,
String commonColumnName,
ReconConfig newReconConfig,
ReconStats newReconStats // can be null
) {
super(cellChanges, commonColumnName, false);
_newReconConfig = newReconConfig;
_newReconStats = newReconStats;
}
public ReconChange(
CellChange cellChange,
String commonColumnName,
ReconConfig newReconConfig,
ReconStats newReconStats // can be null
) {
super(cellChange, commonColumnName, false);
_newReconConfig = newReconConfig;
_newReconStats = newReconStats;
}
@Override
public void apply(Project project) {
synchronized (project) {
super.apply(project);
Column column = project.columnModel.getColumnByName(_commonColumnName);
if (_newReconStats == null) {
_newReconStats = ReconStats.create(project, column.getCellIndex());
}
_oldReconConfig = column.getReconConfig();
_oldReconStats = column.getReconStats();
column.setReconConfig(_newReconConfig);
column.setReconStats(_newReconStats);
}
}
@Override
public void revert(Project project) {
synchronized (project) {
super.revert(project);
Column column = project.columnModel.getColumnByName(_commonColumnName);
column.setReconConfig(_oldReconConfig);
column.setReconStats(_oldReconStats);
}
}
}

View File

@ -8,6 +8,7 @@ import org.json.JSONObject;
import com.metaweb.gridworks.browsing.Engine;
import com.metaweb.gridworks.browsing.FilteredRows;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.history.Change;
import com.metaweb.gridworks.history.HistoryEntry;
import com.metaweb.gridworks.model.Column;
import com.metaweb.gridworks.model.Project;
@ -42,10 +43,13 @@ abstract public class EngineDependentMassCellOperation extends EngineDependentOp
String description = createDescription(column, cellChanges);
MassCellChange massCellChange = new MassCellChange(cellChanges, column.getHeaderLabel(), _updateRowContextDependencies);
return new HistoryEntry(
project, description, this, massCellChange);
project, description, this, createChange(project, column, cellChanges));
}
protected Change createChange(Project project, Column column, List<CellChange> cellChanges) {
return new MassCellChange(
cellChanges, column.getHeaderLabel(), _updateRowContextDependencies);
}
abstract protected RowVisitor createRowVisitor(Project project, List<CellChange> cellChanges) throws Exception;

View File

@ -8,6 +8,7 @@ import org.json.JSONObject;
import org.json.JSONWriter;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.history.Change;
import com.metaweb.gridworks.model.AbstractOperation;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Column;
@ -16,6 +17,7 @@ import com.metaweb.gridworks.model.Recon;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.model.Recon.Judgment;
import com.metaweb.gridworks.model.changes.CellChange;
import com.metaweb.gridworks.model.changes.ReconChange;
public class ReconDiscardJudgmentsOperation extends EngineDependentMassCellOperation {
private static final long serialVersionUID = 6799029731665369179L;
@ -86,4 +88,13 @@ public class ReconDiscardJudgmentsOperation extends EngineDependentMassCellOpera
}
}.init(column.getCellIndex(), cellChanges);
}
protected Change createChange(Project project, Column column, List<CellChange> cellChanges) {
return new ReconChange(
cellChanges,
_columnName,
column.getReconConfig(),
null
);
}
}

View File

@ -1,6 +1,6 @@
package com.metaweb.gridworks.operations;
import java.util.HashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@ -12,6 +12,7 @@ import org.json.JSONWriter;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.expr.ExpressionUtils;
import com.metaweb.gridworks.history.Change;
import com.metaweb.gridworks.model.AbstractOperation;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Column;
@ -21,6 +22,7 @@ import com.metaweb.gridworks.model.ReconCandidate;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.model.Recon.Judgment;
import com.metaweb.gridworks.model.changes.CellChange;
import com.metaweb.gridworks.model.changes.ReconChange;
public class ReconJudgeSimilarCellsOperation extends EngineDependentMassCellOperation {
private static final long serialVersionUID = -5205694623711144436L;
@ -199,4 +201,14 @@ public class ReconJudgeSimilarCellsOperation extends EngineDependentMassCellOper
}
}.init(column.getCellIndex(), cellChanges);
}
protected Change createChange(Project project, Column column, List<CellChange> cellChanges) {
return new ReconChange(
cellChanges,
_columnName,
column.getReconConfig(),
null
);
}
}

View File

@ -10,6 +10,7 @@ import org.json.JSONObject;
import org.json.JSONWriter;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.history.Change;
import com.metaweb.gridworks.model.AbstractOperation;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Column;
@ -18,6 +19,7 @@ import com.metaweb.gridworks.model.Recon;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.model.Recon.Judgment;
import com.metaweb.gridworks.model.changes.CellChange;
import com.metaweb.gridworks.model.changes.ReconChange;
public class ReconMarkNewTopicsOperation extends EngineDependentMassCellOperation {
private static final long serialVersionUID = -5205694623711144436L;
@ -111,4 +113,13 @@ public class ReconMarkNewTopicsOperation extends EngineDependentMassCellOperatio
}
}.init(column.getCellIndex(), cellChanges);
}
protected Change createChange(Project project, Column column, List<CellChange> cellChanges) {
return new ReconChange(
cellChanges,
_columnName,
column.getReconConfig(),
null
);
}
}

View File

@ -8,6 +8,7 @@ import org.json.JSONObject;
import org.json.JSONWriter;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.history.Change;
import com.metaweb.gridworks.model.AbstractOperation;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Column;
@ -16,6 +17,7 @@ import com.metaweb.gridworks.model.ReconCandidate;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.model.Recon.Judgment;
import com.metaweb.gridworks.model.changes.CellChange;
import com.metaweb.gridworks.model.changes.ReconChange;
public class ReconMatchBestCandidatesOperation extends EngineDependentMassCellOperation {
private static final long serialVersionUID = 5393888241057341155L;
@ -91,4 +93,13 @@ public class ReconMatchBestCandidatesOperation extends EngineDependentMassCellOp
}
}.init(column.getCellIndex(), cellChanges);
}
protected Change createChange(Project project, Column column, List<CellChange> cellChanges) {
return new ReconChange(
cellChanges,
_columnName,
column.getReconConfig(),
null
);
}
}

View File

@ -9,6 +9,7 @@ import org.json.JSONObject;
import org.json.JSONWriter;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.history.Change;
import com.metaweb.gridworks.model.AbstractOperation;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Column;
@ -18,6 +19,7 @@ import com.metaweb.gridworks.model.ReconCandidate;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.model.Recon.Judgment;
import com.metaweb.gridworks.model.changes.CellChange;
import com.metaweb.gridworks.model.changes.ReconChange;
public class ReconMatchSpecificTopicOperation extends EngineDependentMassCellOperation {
private static final long serialVersionUID = -5205694623711144436L;
@ -121,4 +123,13 @@ public class ReconMatchSpecificTopicOperation extends EngineDependentMassCellOpe
}
}.init(column.getCellIndex(), cellChanges);
}
protected Change createChange(Project project, Column column, List<CellChange> cellChanges) {
return new ReconChange(
cellChanges,
_columnName,
column.getReconConfig(),
null
);
}
}

View File

@ -35,7 +35,7 @@ import com.metaweb.gridworks.model.ReconConfig;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.model.Recon.Judgment;
import com.metaweb.gridworks.model.changes.CellChange;
import com.metaweb.gridworks.model.changes.MassCellChange;
import com.metaweb.gridworks.model.changes.ReconChange;
import com.metaweb.gridworks.process.LongRunningProcess;
import com.metaweb.gridworks.process.Process;
import com.metaweb.gridworks.util.ParsingUtilities;
@ -115,42 +115,6 @@ public class ReconOperation extends EngineDependentOperation {
}
}
static protected class ReconChange extends MassCellChange {
private static final long serialVersionUID = 7048806528587330543L;
final protected ReconConfig _newReconConfig;
protected ReconConfig _oldReconConfig;
public ReconChange(
List<CellChange> cellChanges,
String commonColumnName,
ReconConfig newReconConfig
) {
super(cellChanges, commonColumnName, false);
_newReconConfig = newReconConfig;
}
@Override
public void apply(Project project) {
synchronized (project) {
super.apply(project);
Column column = project.columnModel.getColumnByName(_commonColumnName);
_oldReconConfig = column.getReconConfig();
column.setReconConfig(_newReconConfig);
}
}
@Override
public void revert(Project project) {
synchronized (project) {
super.revert(project);
project.columnModel.getColumnByName(_commonColumnName)
.setReconConfig(_oldReconConfig);
}
}
}
public class ReconProcess extends LongRunningProcess implements Runnable {
final protected Project _project;
final protected JSONObject _engineConfig;
@ -239,9 +203,12 @@ public class ReconOperation extends EngineDependentOperation {
}
}
ReconConfig reconConfig = new ReconConfig(_typeID, _typeName);
Change reconChange = new ReconChange(cellChanges, _columnName, reconConfig);
Change reconChange = new ReconChange(
cellChanges,
_columnName,
new ReconConfig(_typeID, _typeName),
null
);
HistoryEntry historyEntry = new HistoryEntry(
_project,

View File

@ -80,7 +80,7 @@ ReconDialog.prototype._createDialog = function() {
minScore: minScoreInput[0].value
},
null,
{ cellsChanged: true }
{ cellsChanged: true, columnStatsChanged: true }
);
}
}).appendTo(footer);

View File

@ -89,7 +89,7 @@ Gridworks.createUpdateFunction = function(options, onFinallyDone) {
pushFunction(function(onDone) {
ui.historyWidget.update(onDone);
});
if (options["everythingChanged"] || options["modelsChanged"]) {
if (options["everythingChanged"] || options["modelsChanged"] || options["columnStatsChanged"]) {
pushFunction(Gridworks.reinitializeProjectData);
}
if (options["everythingChanged"] || options["modelsChanged"] || options["rowsChanged"] || options["cellsChanged"] || options["engineChanged"]) {

View File

@ -131,7 +131,7 @@ DataTableCellUI.prototype._doMatchNewTopicToOneCell = function() {
};
DataTableCellUI.prototype._doMatchNewTopicToSimilarCells = function() {
this._doJudgmentForSimilarCells("new", { shareNewTopics: true });
this._doJudgmentForSimilarCells("new", { shareNewTopics: true }, true);
};
DataTableCellUI.prototype._doMatchTopicToOneCell = function(candidate) {
@ -151,7 +151,7 @@ DataTableCellUI.prototype._doMatchTopicToSimilarCells = function(candidate) {
topicName: candidate.name,
score: candidate.score,
types: candidate.types.join(",")
});
}, true);
};
DataTableCellUI.prototype._doJudgment = function(judgment, params) {
@ -159,7 +159,7 @@ DataTableCellUI.prototype._doJudgment = function(judgment, params) {
params.row = this._rowIndex;
params.cell = this._cellIndex;
params.judgment = judgment;
this._postProcessOneCell("recon-judge-one-cell", params);
this._postProcessOneCell("recon-judge-one-cell", params, true);
};
DataTableCellUI.prototype._doJudgmentForSimilarCells = function(judgment, params) {
@ -168,7 +168,7 @@ DataTableCellUI.prototype._doJudgmentForSimilarCells = function(judgment, params
params.similarValue = this._cell.v;
params.judgment = judgment;
this._postProcessSeveralCells("recon-judge-similar-cells", params);
this._postProcessSeveralCells("recon-judge-similar-cells", params, true);
};
DataTableCellUI.prototype._searchForMatch = function() {
@ -205,12 +205,12 @@ DataTableCellUI.prototype._searchForMatch = function() {
params.similarValue = self._cell.v;
params.columnName = Gridworks.cellIndexToColumn(self._cellIndex).headerLabel;
self._postProcessSeveralCells("recon-judge-similar-cells", params);
self._postProcessSeveralCells("recon-judge-similar-cells", params, true);
} else {
params.row = self._rowIndex;
params.cell = self._cellIndex;
self._postProcessOneCell("recon-judge-one-cell", params);
self._postProcessOneCell("recon-judge-one-cell", params, true);
}
DialogSystem.dismissUntil(level - 1);
@ -225,14 +225,14 @@ DataTableCellUI.prototype._searchForMatch = function() {
input.focus().data("suggest").textchange();
};
DataTableCellUI.prototype._postProcessOneCell = function(command, params) {
DataTableCellUI.prototype._postProcessOneCell = function(command, params, columnStatsChanged) {
var self = this;
Gridworks.postProcess(
command,
params,
null,
{},
{ columnStatsChanged: columnStatsChanged },
{
onDone: function(o) {
self._cell = data.cell;
@ -242,12 +242,12 @@ DataTableCellUI.prototype._postProcessOneCell = function(command, params) {
);
};
DataTableCellUI.prototype._postProcessSeveralCells = function(command, params) {
DataTableCellUI.prototype._postProcessSeveralCells = function(command, params, columnStatsChanged) {
Gridworks.postProcess(
command,
params,
null,
{ cellsChanged: true }
{ cellsChanged: true, columnStatsChanged: columnStatsChanged }
);
};

View File

@ -24,6 +24,31 @@ DataTableColumnHeaderUI.prototype._render = function() {
$('<img src="/images/menu-dropdown.png" />').addClass("column-header-menu").appendTo(headerRight).click(function() {
self._createMenuForColumnHeader(this);
});
if ("reconStats" in this._column) {
var stats = this._column.reconStats;
if (stats.nonBlanks > 0) {
var newPercent = Math.ceil(100 * stats.newTopics / stats.nonBlanks);
var matchPercent = Math.ceil(100 * stats.matchedTopics / stats.nonBlanks);
var unreconciledPercent = Math.ceil(100 * (stats.nonBlanks - stats.matchedTopics - stats.newTopics) / stats.nonBlanks);
var whole = $('<div>')
.height("3px")
.css("background", "#333")
.css("position", "relative")
.attr("title", matchPercent + "% matched, " + newPercent + "% new, " + unreconciledPercent + "% to be reconciled")
.width("100%")
.appendTo(td);
$('<div>').height("100%").css("background", "white").css("position", "absolute")
.width(Math.round((stats.newTopics + stats.matchedTopics) * 100 / stats.nonBlanks) + "%")
.appendTo(whole);
$('<div>').height("100%").css("background", "#6d6").css("position", "absolute")
.width(Math.round(stats.matchedTopics * 100 / stats.nonBlanks) + "%")
.appendTo(whole);
}
}
};
DataTableColumnHeaderUI.prototype._createMenuForColumnHeader = function(elmt) {
@ -452,7 +477,7 @@ DataTableColumnHeaderUI.prototype._doReconDiscardJudgments = function() {
"recon-discard-judgments",
{ columnName: this._column.headerLabel },
null,
{ cellsChanged: true }
{ cellsChanged: true, columnStatsChanged: true }
);
};
@ -461,7 +486,7 @@ DataTableColumnHeaderUI.prototype._doReconMatchBestCandidates = function() {
"recon-match-best-candidates",
{ columnName: this._column.headerLabel },
null,
{ cellsChanged: true }
{ cellsChanged: true, columnStatsChanged: true }
);
};
@ -470,7 +495,7 @@ DataTableColumnHeaderUI.prototype._doReconMarkNewTopics = function(shareNewTopic
"recon-mark-new-topics",
{ columnName: this._column.headerLabel, shareNewTopics: shareNewTopics },
null,
{ cellsChanged: true }
{ cellsChanged: true, columnStatsChanged: true }
);
};
@ -498,7 +523,7 @@ DataTableColumnHeaderUI.prototype._doSearchToMatch = function() {
types: $.map(data.type, function(elmt) { return elmt.id; }).join(",")
},
null,
{ cellsChanged: true }
{ cellsChanged: true, columnStatsChanged: true }
);
DialogSystem.dismissUntil(level - 1);