From 00696a96fc21de31a5461fbcc3d667738a357af8 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Tue, 2 Feb 2010 20:50:35 +0000 Subject: [PATCH] Added commands to approve, approve new, and discard recon candidates. Support sections in menus. git-svn-id: http://google-refine.googlecode.com/svn/trunk@24 7d457c2a-affb-35e4-300a-418c747d4874 --- .../com/metaweb/gridlock/GridlockServlet.java | 7 + .../commands/ApproveNewReconcileCommand.java | 86 +++++ .../commands/ApproveReconcileCommand.java | 86 +++++ .../commands/DiscardReconcileCommand.java | 82 ++++ .../com/metaweb/gridlock/model/Recon.java | 21 +- .../gridlock/model/ReconCandidate.java | 2 +- .../webapp/scripts/project/data-table-view.js | 360 +++++++++++------- src/main/webapp/scripts/util/menu.js | 5 + src/main/webapp/styles/browsing.css | 5 + src/main/webapp/styles/common.css | 9 +- 10 files changed, 514 insertions(+), 149 deletions(-) create mode 100644 src/main/java/com/metaweb/gridlock/commands/ApproveNewReconcileCommand.java create mode 100644 src/main/java/com/metaweb/gridlock/commands/ApproveReconcileCommand.java create mode 100644 src/main/java/com/metaweb/gridlock/commands/DiscardReconcileCommand.java diff --git a/src/main/java/com/metaweb/gridlock/GridlockServlet.java b/src/main/java/com/metaweb/gridlock/GridlockServlet.java index 5ed0a9ee9..26958398a 100644 --- a/src/main/java/com/metaweb/gridlock/GridlockServlet.java +++ b/src/main/java/com/metaweb/gridlock/GridlockServlet.java @@ -14,9 +14,12 @@ import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; +import com.metaweb.gridlock.commands.ApproveNewReconcileCommand; +import com.metaweb.gridlock.commands.ApproveReconcileCommand; import com.metaweb.gridlock.commands.Command; import com.metaweb.gridlock.commands.ComputeFacetsCommand; import com.metaweb.gridlock.commands.CreateProjectFromUploadCommand; +import com.metaweb.gridlock.commands.DiscardReconcileCommand; import com.metaweb.gridlock.commands.DoTextTransformCommand; import com.metaweb.gridlock.commands.GetColumnModelCommand; import com.metaweb.gridlock.commands.GetHistoryCommand; @@ -43,7 +46,11 @@ public class GridlockServlet extends HttpServlet { _commands.put("undo-redo", new UndoRedoCommand()); _commands.put("compute-facets", new ComputeFacetsCommand()); _commands.put("do-text-transform", new DoTextTransformCommand()); + _commands.put("reconcile", new ReconcileCommand()); + _commands.put("approve-reconcile", new ApproveReconcileCommand()); + _commands.put("approve-new-reconcile", new ApproveNewReconcileCommand()); + _commands.put("discard-reconcile", new DiscardReconcileCommand()); } @Override diff --git a/src/main/java/com/metaweb/gridlock/commands/ApproveNewReconcileCommand.java b/src/main/java/com/metaweb/gridlock/commands/ApproveNewReconcileCommand.java new file mode 100644 index 000000000..ee1c67bea --- /dev/null +++ b/src/main/java/com/metaweb/gridlock/commands/ApproveNewReconcileCommand.java @@ -0,0 +1,86 @@ +package com.metaweb.gridlock.commands; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.metaweb.gridlock.browsing.Engine; +import com.metaweb.gridlock.browsing.FilteredRows; +import com.metaweb.gridlock.browsing.RowVisitor; +import com.metaweb.gridlock.history.CellChange; +import com.metaweb.gridlock.history.HistoryEntry; +import com.metaweb.gridlock.history.MassCellChange; +import com.metaweb.gridlock.model.Cell; +import com.metaweb.gridlock.model.Column; +import com.metaweb.gridlock.model.Project; +import com.metaweb.gridlock.model.Recon; +import com.metaweb.gridlock.model.Row; +import com.metaweb.gridlock.model.Recon.Judgment; +import com.metaweb.gridlock.process.QuickHistoryEntryProcess; + +public class ApproveNewReconcileCommand extends Command { + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + try { + Project project = getProject(request); + Engine engine = getEngine(request, project); + + int cellIndex = Integer.parseInt(request.getParameter("cell")); + Column column = project.columnModel.getColumnByCellIndex(cellIndex); + if (column == null) { + respond(response, "{ \"code\" : \"error\", \"message\" : \"No such column\" }"); + return; + } + + String columnName = column.headerLabel; + List cellChanges = new ArrayList(project.rows.size()); + + FilteredRows filteredRows = engine.getAllFilteredRows(); + filteredRows.accept(project, new RowVisitor() { + int cellIndex; + List cellChanges; + + public RowVisitor init(int cellIndex, List cellChanges) { + this.cellIndex = cellIndex; + this.cellChanges = cellChanges; + return this; + } + + @Override + public boolean visit(Project project, int rowIndex, Row row) { + if (cellIndex < row.cells.size()) { + Cell cell = row.cells.get(cellIndex); + + Cell newCell = new Cell(); + newCell.value = cell.value; + newCell.recon = cell.recon != null ? cell.recon.dup() : new Recon(); + newCell.recon.match = null; + newCell.recon.judgment = Judgment.New; + + CellChange cellChange = new CellChange(rowIndex, cellIndex, cell, newCell); + cellChanges.add(cellChange); + } + return false; + } + }.init(cellIndex, cellChanges)); + + MassCellChange massCellChange = new MassCellChange(cellChanges); + HistoryEntry historyEntry = new HistoryEntry( + project, "Approve new topics for " + columnName, massCellChange); + + boolean done = project.processManager.queueProcess( + new QuickHistoryEntryProcess(project, historyEntry)); + + respond(response, "{ \"code\" : " + (done ? "\"ok\"" : "\"pending\"") + " }"); + } catch (Exception e) { + respondException(response, e); + } + } +} diff --git a/src/main/java/com/metaweb/gridlock/commands/ApproveReconcileCommand.java b/src/main/java/com/metaweb/gridlock/commands/ApproveReconcileCommand.java new file mode 100644 index 000000000..0c19f0053 --- /dev/null +++ b/src/main/java/com/metaweb/gridlock/commands/ApproveReconcileCommand.java @@ -0,0 +1,86 @@ +package com.metaweb.gridlock.commands; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.metaweb.gridlock.browsing.Engine; +import com.metaweb.gridlock.browsing.FilteredRows; +import com.metaweb.gridlock.browsing.RowVisitor; +import com.metaweb.gridlock.history.CellChange; +import com.metaweb.gridlock.history.HistoryEntry; +import com.metaweb.gridlock.history.MassCellChange; +import com.metaweb.gridlock.model.Cell; +import com.metaweb.gridlock.model.Column; +import com.metaweb.gridlock.model.Project; +import com.metaweb.gridlock.model.Row; +import com.metaweb.gridlock.model.Recon.Judgment; +import com.metaweb.gridlock.process.QuickHistoryEntryProcess; + +public class ApproveReconcileCommand extends Command { + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + try { + Project project = getProject(request); + Engine engine = getEngine(request, project); + + int cellIndex = Integer.parseInt(request.getParameter("cell")); + Column column = project.columnModel.getColumnByCellIndex(cellIndex); + if (column == null) { + respond(response, "{ \"code\" : \"error\", \"message\" : \"No such column\" }"); + return; + } + + String columnName = column.headerLabel; + List cellChanges = new ArrayList(project.rows.size()); + + FilteredRows filteredRows = engine.getAllFilteredRows(); + filteredRows.accept(project, new RowVisitor() { + int cellIndex; + List cellChanges; + + public RowVisitor init(int cellIndex, List cellChanges) { + this.cellIndex = cellIndex; + this.cellChanges = cellChanges; + return this; + } + + @Override + public boolean visit(Project project, int rowIndex, Row row) { + if (cellIndex < row.cells.size()) { + Cell cell = row.cells.get(cellIndex); + if (cell.recon != null && cell.recon.candidates.size() > 0) { + Cell newCell = new Cell(); + newCell.value = cell.value; + newCell.recon = cell.recon.dup(); + newCell.recon.match = newCell.recon.candidates.get(0); + newCell.recon.judgment = Judgment.Approve; + + CellChange cellChange = new CellChange(rowIndex, cellIndex, cell, newCell); + cellChanges.add(cellChange); + } + } + return false; + } + }.init(cellIndex, cellChanges)); + + MassCellChange massCellChange = new MassCellChange(cellChanges); + HistoryEntry historyEntry = new HistoryEntry( + project, "Approve best recon candidates for " + columnName, massCellChange); + + boolean done = project.processManager.queueProcess( + new QuickHistoryEntryProcess(project, historyEntry)); + + respond(response, "{ \"code\" : " + (done ? "\"ok\"" : "\"pending\"") + " }"); + } catch (Exception e) { + respondException(response, e); + } + } +} diff --git a/src/main/java/com/metaweb/gridlock/commands/DiscardReconcileCommand.java b/src/main/java/com/metaweb/gridlock/commands/DiscardReconcileCommand.java new file mode 100644 index 000000000..ed632e5f0 --- /dev/null +++ b/src/main/java/com/metaweb/gridlock/commands/DiscardReconcileCommand.java @@ -0,0 +1,82 @@ +package com.metaweb.gridlock.commands; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.metaweb.gridlock.browsing.Engine; +import com.metaweb.gridlock.browsing.FilteredRows; +import com.metaweb.gridlock.browsing.RowVisitor; +import com.metaweb.gridlock.history.CellChange; +import com.metaweb.gridlock.history.HistoryEntry; +import com.metaweb.gridlock.history.MassCellChange; +import com.metaweb.gridlock.model.Cell; +import com.metaweb.gridlock.model.Column; +import com.metaweb.gridlock.model.Project; +import com.metaweb.gridlock.model.Row; +import com.metaweb.gridlock.process.QuickHistoryEntryProcess; + +public class DiscardReconcileCommand extends Command { + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + try { + Project project = getProject(request); + Engine engine = getEngine(request, project); + + int cellIndex = Integer.parseInt(request.getParameter("cell")); + Column column = project.columnModel.getColumnByCellIndex(cellIndex); + if (column == null) { + respond(response, "{ \"code\" : \"error\", \"message\" : \"No such column\" }"); + return; + } + + String columnName = column.headerLabel; + List cellChanges = new ArrayList(project.rows.size()); + + FilteredRows filteredRows = engine.getAllFilteredRows(); + filteredRows.accept(project, new RowVisitor() { + int cellIndex; + List cellChanges; + + public RowVisitor init(int cellIndex, List cellChanges) { + this.cellIndex = cellIndex; + this.cellChanges = cellChanges; + return this; + } + + @Override + public boolean visit(Project project, int rowIndex, Row row) { + if (cellIndex < row.cells.size()) { + Cell cell = row.cells.get(cellIndex); + + Cell newCell = new Cell(); + newCell.value = cell.value; + newCell.recon = null; + + CellChange cellChange = new CellChange(rowIndex, cellIndex, cell, newCell); + cellChanges.add(cellChange); + } + return false; + } + }.init(cellIndex, cellChanges)); + + MassCellChange massCellChange = new MassCellChange(cellChanges); + HistoryEntry historyEntry = new HistoryEntry( + project, "Discard recon results for " + columnName, massCellChange); + + boolean done = project.processManager.queueProcess( + new QuickHistoryEntryProcess(project, historyEntry)); + + respond(response, "{ \"code\" : " + (done ? "\"ok\"" : "\"pending\"") + " }"); + } catch (Exception e) { + respondException(response, e); + } + } +} diff --git a/src/main/java/com/metaweb/gridlock/model/Recon.java b/src/main/java/com/metaweb/gridlock/model/Recon.java index 3d01c3e98..b7eae8b06 100644 --- a/src/main/java/com/metaweb/gridlock/model/Recon.java +++ b/src/main/java/com/metaweb/gridlock/model/Recon.java @@ -27,6 +27,15 @@ public class Recon implements Serializable, HasFields, Jsonizable { public Judgment judgment = Judgment.None; public ReconCandidate match = null; + public Recon dup() { + Recon r = new Recon(); + r.features.putAll(new HashMap(features)); + r.candidates.addAll(candidates); + r.judgment = judgment; + r.match = match; + return r; + } + @Override public Object getField(String name, Properties bindings) { if ("best".equals(name)) { @@ -75,15 +84,15 @@ public class Recon implements Serializable, HasFields, Jsonizable { writer.key("j"); writer.value(judgmentToString()); - writer.key("c"); writer.array(); - for (ReconCandidate c : candidates) { - c.write(writer, options); - } - writer.endArray(); - if (match != null) { writer.key("m"); match.write(writer, options); + } else { + writer.key("c"); writer.array(); + for (ReconCandidate c : candidates) { + c.write(writer, options); + } + writer.endArray(); } writer.endObject(); diff --git a/src/main/java/com/metaweb/gridlock/model/ReconCandidate.java b/src/main/java/com/metaweb/gridlock/model/ReconCandidate.java index 129b50e67..06d305b78 100644 --- a/src/main/java/com/metaweb/gridlock/model/ReconCandidate.java +++ b/src/main/java/com/metaweb/gridlock/model/ReconCandidate.java @@ -42,7 +42,7 @@ public class ReconCandidate implements Serializable, HasFields, Jsonizable { writer.key("id"); writer.value(topicID); //writer.key("guid"); writer.value(topicGUID); writer.key("name"); writer.value(topicName); - //writer.key("score"); writer.value(score); + writer.key("score"); writer.value(score); /* writer.key("types"); writer.array(); diff --git a/src/main/webapp/scripts/project/data-table-view.js b/src/main/webapp/scripts/project/data-table-view.js index 9c20e7047..2851c9ef9 100644 --- a/src/main/webapp/scripts/project/data-table-view.js +++ b/src/main/webapp/scripts/project/data-table-view.js @@ -5,6 +5,10 @@ function DataTableView(div) { this._showRows(0); } +DataTableView.prototype.update = function(reset) { + this._showRows(reset ? 0 : theProject.rowModel.start); +}; + DataTableView.prototype.render = function() { var self = this; var container = this._div.empty(); @@ -115,20 +119,36 @@ DataTableView.prototype.render = function() { return; } - $(td).html(cell.v); - - if ("r" in cell && self._showRecon) { - var candidates = cell.r.c; - var ul = $('
    ').appendTo(td); - - for (var i = 0; i < candidates.length; i++) { - var candidate = candidates[i]; - var li = $('
  • ').appendTo(ul); + if (!("r" in cell) || cell.r == null) { + $(td).html(cell.v); + } else { + var r = cell.r; + if (r.j == "new") { + $(td).html(cell.v + " (new)"); + } else if (r.j == "approve" && "m" in r && r.m != null) { + var match = cell.r.m; $('') - .attr("href", "http://www.freebase.com/view" + candidate.id) + .attr("href", "http://www.freebase.com/view" + match.id) .attr("target", "_blank") - .text(candidate.name) - .appendTo(li); + .text(match.name) + .appendTo(td); + } else { + $(td).html(cell.v); + if (self._showRecon && "c" in r && r.c.length > 0) { + var candidates = r.c; + var ul = $('
      ').appendTo(td); + + for (var i = 0; i < candidates.length; i++) { + var candidate = candidates[i]; + var li = $('
    • ').appendTo(ul); + $('') + .attr("href", "http://www.freebase.com/view" + candidate.id) + .attr("target", "_blank") + .text(candidate.name) + .appendTo(li); + $('').addClass("recon-score").text("(" + Math.round(candidate.score) + ")").appendTo(li); + } + } } } }; @@ -221,8 +241,9 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm { label: "Filter", submenu: [ + { "heading" : "On Cell Content" }, { - label: "By Nominal Choices", + label: "Simple Facet", click: function() { ui.browsingEngine.addFacet( "list", @@ -234,9 +255,18 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm ); } }, + { + label: "Custom Facet ...", + click: function() { + var expression = window.prompt("Enter expression", 'value'); + if (expression != null) { + self._doFilterByExpression(column, expression); + } + } + }, {}, { - label: "By Simple Text Search", + label: "Text Search", click: function() { ui.browsingEngine.addFacet( "text", @@ -250,12 +280,12 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm } }, { - label: "By Regular Expression", + label: "Regular Expression Search", click: function() { ui.browsingEngine.addFacet( "text", { - "name" : column.headerLabel, + "name" : column.headerLabel + " (regex)", "cellIndex" : column.cellIndex, "mode" : "regex", "caseSensitive" : true @@ -263,121 +293,123 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm ); } }, + { "heading" : "By Reconciliation Features" }, { - label: "By Custom Expression ...", + label: "By Judgment", click: function() { - var expression = window.prompt("Enter expression", 'value'); - if (expression != null) { - self._doFilterByExpression(column, expression); - } + ui.browsingEngine.addFacet( + "list", + { + "name" : column.headerLabel + ": judgment", + "cellIndex" : column.cellIndex, + "expression" : "cell.recon.judgment" + } + ); } }, {}, { - label: "By Reconciliation Features", - submenu: [ - { - label: "By Type Match", - click: function() { - ui.browsingEngine.addFacet( - "list", - { - "name" : column.headerLabel + ": recon type match", - "cellIndex" : column.cellIndex, - "expression" : "cell.recon.features.typeMatch" - }, - { - "scroll" : false - } - ); + label: "Best Candidate's Relevance Score", + click: function() { + ui.browsingEngine.addFacet( + "range", + { + "name" : column.headerLabel + ": best candidate's relevance score", + "cellIndex" : column.cellIndex, + "expression" : "cell.recon.best.score", + "mode" : "range", + "min" : 0, + "max" : 200 + }, + { } - }, - { - label: "By Name Match", - click: function() { - ui.browsingEngine.addFacet( - "list", - { - "name" : column.headerLabel + ": recon name match", - "cellIndex" : column.cellIndex, - "expression" : "cell.recon.features.nameMatch" - }, - { - "scroll" : false - } - ); + ); + } + }, + { + label: "Best Candidate's Type Match", + click: function() { + ui.browsingEngine.addFacet( + "list", + { + "name" : column.headerLabel + ": best candidate's type match", + "cellIndex" : column.cellIndex, + "expression" : "cell.recon.features.typeMatch" + }, + { + "scroll" : false } - }, - { - label: "By Best Candidate's Score", - click: function() { - ui.browsingEngine.addFacet( - "range", - { - "name" : column.headerLabel + ": best candidate's score", - "cellIndex" : column.cellIndex, - "expression" : "cell.recon.best.score", - "mode" : "range", - "min" : 0, - "max" : 200 - }, - { - } - ); + ); + } + }, + { + label: "Best Candidate's Name Match", + click: function() { + ui.browsingEngine.addFacet( + "list", + { + "name" : column.headerLabel + ": best candidate's name match", + "cellIndex" : column.cellIndex, + "expression" : "cell.recon.features.nameMatch" + }, + { + "scroll" : false } - }, - { - label: "By Name's Edit Distance", - click: function() { - ui.browsingEngine.addFacet( - "range", - { - "name" : column.headerLabel + ": name's edit distance", - "cellIndex" : column.cellIndex, - "expression" : "cell.recon.features.nameLevenshtein", - "mode" : "range", - "min" : 0, - "max" : 1, - "step" : 0.1 - }, - { - } - ); + ); + } + }, + {}, + { + label: "Best Candidate's Name Edit Distance", + click: function() { + ui.browsingEngine.addFacet( + "range", + { + "name" : column.headerLabel + ": best candidate's name edit distance", + "cellIndex" : column.cellIndex, + "expression" : "cell.recon.features.nameLevenshtein", + "mode" : "range", + "min" : 0, + "max" : 1, + "step" : 0.1 + }, + { } - }, - { - label: "By Name's Word Similarity", - click: function() { - ui.browsingEngine.addFacet( - "range", - { - "name" : column.headerLabel + ": name's word similarity", - "cellIndex" : column.cellIndex, - "expression" : "cell.recon.features.nameWordDistance", - "mode" : "range", - "min" : 0, - "max" : 1, - "step" : 0.1 - }, - { - } - ); + ); + } + }, + { + label: "Best Candidate's Name Word Similarity", + click: function() { + ui.browsingEngine.addFacet( + "range", + { + "name" : column.headerLabel + ": best candidate's name word similarity", + "cellIndex" : column.cellIndex, + "expression" : "cell.recon.features.nameWordDistance", + "mode" : "range", + "min" : 0, + "max" : 1, + "step" : 0.1 + }, + { } - }, - { - label: "By Best Candidate's Types", - click: function() { - ui.browsingEngine.addFacet( - "list", - { - "name" : column.headerLabel + ": best candidate's types", - "cellIndex" : column.cellIndex, - "expression" : "cell.recon.best.type" - } - ); + ); + } + }, + {}, + { + label: "Best Candidate's Types", + click: function() { + ui.browsingEngine.addFacet( + "list", + { + "name" : column.headerLabel + ": best candidate's types", + "cellIndex" : column.cellIndex, + "expression" : "cell.recon.best.type" } - } - ] + ); + } } ] }, @@ -445,38 +477,39 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm label: "Reconcile", submenu: [ { - label: "Initiate on Filtered Rows...", + label: "Start Reconciling ...", + tooltip: "Reconcile text in this column with topics on Freebase", click: function() { new ReconDialog(index); } }, {}, { - label: "Approve Filtered Rows", - click: function() {} + label: "Approve Best Candidates", + tooltip: "Approve best reconciliaton candidate per cell in this column for all current filtered rows", + click: function() { + self._doApproveBestCandidates(column); + } + }, + { + label: "Approve As New Topics", + tooltip: "Set to create new topics for cells in this column for all current filtered rows", + click: function() { + self._doApproveNewTopics(column); + } + }, + { + label: "Discard Reconciliation Results", + tooltip: "Discard reconciliaton results in this column for all current filtered rows", + click: function() { + self._doDiscardReconResults(column); + } } ] } ], elmt); }; -DataTableView.prototype._doTextTransform = function(column, expression) { - var self = this; - $.post( - "/command/do-text-transform?" + $.param({ project: theProject.id, cell: column.cellIndex, expression: expression }), - { engine: JSON.stringify(ui.browsingEngine.getJSON()) }, - function(data) { - if (data.code == "ok") { - self.update(); - ui.historyWidget.update(); - } else { - ui.processWidget.update(); - } - }, - "json" - ); -}; - DataTableView.prototype._doFilterByExpression = function(column, expression) { ui.browsingEngine.addFacet( "list", @@ -488,7 +521,52 @@ DataTableView.prototype._doFilterByExpression = function(column, expression) { ); }; -DataTableView.prototype.update = function(reset) { - this._showRows(reset ? 0 : theProject.rowModel.start); +DataTableView.prototype._createUpdateFunction = function() { + var self = this; + return function(data) { + if (data.code == "ok") { + self.update(); + ui.historyWidget.update(); + } else { + ui.processWidget.update(); + } + }; }; +DataTableView.prototype._doPostThenUpdate = function(command, params) { + params.project = theProject.id; + $.post( + "/command/" + command + "?" + $.param(params), + { engine: JSON.stringify(ui.browsingEngine.getJSON()) }, + this._createUpdateFunction(), + "json" + ); +}; + +DataTableView.prototype._doTextTransform = function(column, expression) { + this._doPostThenUpdate( + "do-text-transform", + { cell: column.cellIndex, expression: expression } + ); +}; + +DataTableView.prototype._doDiscardReconResults = function(column) { + this._doPostThenUpdate( + "discard-reconcile", + { cell: column.cellIndex } + ); +}; + +DataTableView.prototype._doApproveBestCandidates = function(column) { + this._doPostThenUpdate( + "approve-reconcile", + { cell: column.cellIndex } + ); +}; + +DataTableView.prototype._doApproveNewTopics = function(column) { + this._doPostThenUpdate( + "approve-new-reconcile", + { cell: column.cellIndex } + ); +}; diff --git a/src/main/webapp/scripts/util/menu.js b/src/main/webapp/scripts/util/menu.js index a2ef9313d..7664f2e98 100644 --- a/src/main/webapp/scripts/util/menu.js +++ b/src/main/webapp/scripts/util/menu.js @@ -78,7 +78,12 @@ MenuSystem.createAndShowStandardMenu = function(items, elmt, options) { }); } else { menuItem.html(item.label).click(item.click); + if ("tooltip" in item) { + menuItem.attr("title", item.tooltip); + } } + } else if ("heading" in item) { + $('
      ').addClass("menu-section").text(item.heading).appendTo(menu); } else { $('
      ').appendTo(menu); } diff --git a/src/main/webapp/styles/browsing.css b/src/main/webapp/styles/browsing.css index 372dfd971..79ef7419c 100644 --- a/src/main/webapp/styles/browsing.css +++ b/src/main/webapp/styles/browsing.css @@ -86,3 +86,8 @@ a.facet-choice-link:hover { .facet-text-body input { width: 98%; } + +.recon-score { + color: #aaa; + margin-left: 0.5em; +} \ No newline at end of file diff --git a/src/main/webapp/styles/common.css b/src/main/webapp/styles/common.css index 6b164bfa4..cdf477fd4 100644 --- a/src/main/webapp/styles/common.css +++ b/src/main/webapp/styles/common.css @@ -59,7 +59,7 @@ a.inaction { .menu-container { position: absolute; - width: 200px; + width: 250px; background: white; padding: 1px; border: 1px solid #ddd; @@ -90,6 +90,13 @@ a.menu-item img { border: none; } +.menu-section { + padding: 2px 7px; + background: #aaa; + color: white; + font-weight: bold; +} + .dialog-overlay { background: black; opacity: 0.3;