From 06b537315180d0cd65608eb8359687f89254c3a0 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Mon, 1 Feb 2010 03:22:35 +0000 Subject: [PATCH] Client-side UI widget for long running processes. git-svn-id: http://google-refine.googlecode.com/svn/trunk@16 7d457c2a-affb-35e4-300a-418c747d4874 --- .../com/metaweb/gridlock/GridlockServlet.java | 6 +- .../commands/DoTextTransformCommand.java | 11 ++-- .../commands/GetProcessesCommand.java | 29 ++++++++++ .../gridlock/history/HistoryProcess.java | 3 +- .../metaweb/gridlock/model/ColumnModel.java | 9 +++ .../gridlock/process/LongRunningProcess.java | 57 ++++++++++++++++++ .../com/metaweb/gridlock/process/Process.java | 12 +++- .../gridlock/process/ProcessManager.java | 38 ++++++++++++ .../process/QuickHistoryEntryProcess.java | 32 +++++++++- src/main/webapp/project.html | 2 +- src/main/webapp/scripts/project.js | 2 + .../webapp/scripts/project/data-table-view.js | 2 +- .../webapp/scripts/project/process-widget.js | 58 +++++++++++++++++++ src/main/webapp/styles/process.css | 15 +++++ 14 files changed, 264 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/metaweb/gridlock/commands/GetProcessesCommand.java create mode 100644 src/main/java/com/metaweb/gridlock/process/LongRunningProcess.java create mode 100644 src/main/webapp/scripts/project/process-widget.js create mode 100644 src/main/webapp/styles/process.css diff --git a/src/main/java/com/metaweb/gridlock/GridlockServlet.java b/src/main/java/com/metaweb/gridlock/GridlockServlet.java index 7071a65c4..1e8a3e3bf 100644 --- a/src/main/java/com/metaweb/gridlock/GridlockServlet.java +++ b/src/main/java/com/metaweb/gridlock/GridlockServlet.java @@ -20,6 +20,7 @@ import com.metaweb.gridlock.commands.CreateProjectFromUploadCommand; import com.metaweb.gridlock.commands.DoTextTransformCommand; import com.metaweb.gridlock.commands.GetColumnModelCommand; import com.metaweb.gridlock.commands.GetHistoryCommand; +import com.metaweb.gridlock.commands.GetProcessesCommand; import com.metaweb.gridlock.commands.GetProjectMetadataCommand; import com.metaweb.gridlock.commands.GetRowsCommand; import com.metaweb.gridlock.commands.UndoRedoCommand; @@ -31,12 +32,15 @@ public class GridlockServlet extends HttpServlet { static { _commands.put("create-project-from-upload", new CreateProjectFromUploadCommand()); + _commands.put("get-project-metadata", new GetProjectMetadataCommand()); _commands.put("get-column-model", new GetColumnModelCommand()); _commands.put("get-rows", new GetRowsCommand()); + _commands.put("get-processes", new GetProcessesCommand()); _commands.put("get-history", new GetHistoryCommand()); - _commands.put("compute-facets", new ComputeFacetsCommand()); + _commands.put("undo-redo", new UndoRedoCommand()); + _commands.put("compute-facets", new ComputeFacetsCommand()); _commands.put("do-text-transform", new DoTextTransformCommand()); } diff --git a/src/main/java/com/metaweb/gridlock/commands/DoTextTransformCommand.java b/src/main/java/com/metaweb/gridlock/commands/DoTextTransformCommand.java index f46f445cf..c07fb26e8 100644 --- a/src/main/java/com/metaweb/gridlock/commands/DoTextTransformCommand.java +++ b/src/main/java/com/metaweb/gridlock/commands/DoTextTransformCommand.java @@ -30,14 +30,13 @@ public class DoTextTransformCommand extends Command { Project project = getProject(request); int cellIndex = Integer.parseInt(request.getParameter("cell")); - String columnName = null; - for (Column column : project.columnModel.columns) { - if (column.cellIndex == cellIndex) { - columnName = column.headerLabel; - break; - } + Column column = project.columnModel.getColumnByCellIndex(cellIndex); + if (column == null) { + respond(response, "{ \"code\" : \"error\", \"message\" : \"No such column\" }"); + return; } + String columnName = column.headerLabel; String expression = request.getParameter("expression"); try { diff --git a/src/main/java/com/metaweb/gridlock/commands/GetProcessesCommand.java b/src/main/java/com/metaweb/gridlock/commands/GetProcessesCommand.java new file mode 100644 index 000000000..8f22d0b03 --- /dev/null +++ b/src/main/java/com/metaweb/gridlock/commands/GetProcessesCommand.java @@ -0,0 +1,29 @@ +package com.metaweb.gridlock.commands; + +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.JSONException; + +import com.metaweb.gridlock.model.Project; + +public class GetProcessesCommand extends Command { + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + Project project = getProject(request); + + try { + Properties options = new Properties(); + + respondJSON(response, project.processManager.getJSON(options)); + } catch (JSONException e) { + respondException(response, e); + } + } +} diff --git a/src/main/java/com/metaweb/gridlock/history/HistoryProcess.java b/src/main/java/com/metaweb/gridlock/history/HistoryProcess.java index a29e17cb8..2be1d80ef 100644 --- a/src/main/java/com/metaweb/gridlock/history/HistoryProcess.java +++ b/src/main/java/com/metaweb/gridlock/history/HistoryProcess.java @@ -2,6 +2,7 @@ package com.metaweb.gridlock.history; import com.metaweb.gridlock.model.Project; import com.metaweb.gridlock.process.Process; +import com.metaweb.gridlock.process.ProcessManager; public class HistoryProcess extends Process { final protected Project _project; @@ -27,6 +28,6 @@ public class HistoryProcess extends Process { } @Override - public void startPerforming() { + public void startPerforming(ProcessManager manager) { } } diff --git a/src/main/java/com/metaweb/gridlock/model/ColumnModel.java b/src/main/java/com/metaweb/gridlock/model/ColumnModel.java index 6ea7d8cd2..6f2bfdae8 100644 --- a/src/main/java/com/metaweb/gridlock/model/ColumnModel.java +++ b/src/main/java/com/metaweb/gridlock/model/ColumnModel.java @@ -37,4 +37,13 @@ public class ColumnModel implements Serializable { } return _nameToColumn.get(name); } + + public Column getColumnByCellIndex(int cellIndex) { + for (Column column : columns) { + if (column.cellIndex == cellIndex) { + return column; + } + } + return null; + } } diff --git a/src/main/java/com/metaweb/gridlock/process/LongRunningProcess.java b/src/main/java/com/metaweb/gridlock/process/LongRunningProcess.java new file mode 100644 index 000000000..9f86a5cbb --- /dev/null +++ b/src/main/java/com/metaweb/gridlock/process/LongRunningProcess.java @@ -0,0 +1,57 @@ +package com.metaweb.gridlock.process; + +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONObject; + +abstract public class LongRunningProcess extends Process { + final protected String _description; + protected ProcessManager _manager; + protected Thread _thread; + protected int _progress; // out of 100 + + protected LongRunningProcess(String description) { + _description = description; + } + + @Override + public void cancel() { + // TODO Auto-generated method stub + + } + + @Override + JSONObject getJSON(Properties options) throws JSONException { + JSONObject o = new JSONObject(); + + o.put("description", _description); + o.put("immediate", false); + o.put("status", _thread == null ? "pending" : (_thread.isAlive() ? "running" : "done")); + o.put("progress", _progress); + + return o; + } + + @Override + public boolean isImmediate() { + return false; + } + + @Override + public void performImmediate() { + throw new RuntimeException("Not an immediate process"); + } + + @Override + public void startPerforming(ProcessManager manager) { + if (_thread == null) { + _manager = manager; + + _thread = new Thread(getRunnable()); + _thread.start(); + } + } + + abstract protected Runnable getRunnable(); +} diff --git a/src/main/java/com/metaweb/gridlock/process/Process.java b/src/main/java/com/metaweb/gridlock/process/Process.java index f8e5d9daa..cc3592955 100644 --- a/src/main/java/com/metaweb/gridlock/process/Process.java +++ b/src/main/java/com/metaweb/gridlock/process/Process.java @@ -1,10 +1,20 @@ package com.metaweb.gridlock.process; +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONObject; + public abstract class Process { abstract public boolean isImmediate(); + abstract public boolean isRunning(); + abstract public boolean isDone(); + abstract public void performImmediate(); - abstract public void startPerforming(); + abstract public void startPerforming(ProcessManager manager); abstract public void cancel(); + + abstract JSONObject getJSON(Properties options) throws JSONException; } diff --git a/src/main/java/com/metaweb/gridlock/process/ProcessManager.java b/src/main/java/com/metaweb/gridlock/process/ProcessManager.java index 6f58ee7c4..8922c8c3d 100644 --- a/src/main/java/com/metaweb/gridlock/process/ProcessManager.java +++ b/src/main/java/com/metaweb/gridlock/process/ProcessManager.java @@ -1,7 +1,12 @@ package com.metaweb.gridlock.process; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONObject; public class ProcessManager { protected List _processes = new LinkedList(); @@ -10,6 +15,18 @@ public class ProcessManager { } + public JSONObject getJSON(Properties options) throws JSONException { + JSONObject o = new JSONObject(); + + List a = new ArrayList(_processes.size()); + for (Process p : _processes) { + a.add(p.getJSON(options)); + } + o.put("processes", a); + + return o; + } + public boolean queueProcess(Process process) { if (process.isImmediate() && _processes.size() == 0) { process.performImmediate(); @@ -19,4 +36,25 @@ public class ProcessManager { return false; } } + + public void onDoneProcess(Process p) { + update(); + } + + protected void update() { + while (_processes.size() > 0) { + Process p = _processes.get(0); + if (p.isImmediate()) { + p.performImmediate(); + _processes.remove(0); + } else if (p.isDone()) { + _processes.remove(0); + } else { + if (!p.isRunning()) { + p.startPerforming(this); + } + break; + } + } + } } diff --git a/src/main/java/com/metaweb/gridlock/process/QuickHistoryEntryProcess.java b/src/main/java/com/metaweb/gridlock/process/QuickHistoryEntryProcess.java index 80818fa54..f4c73cdf5 100644 --- a/src/main/java/com/metaweb/gridlock/process/QuickHistoryEntryProcess.java +++ b/src/main/java/com/metaweb/gridlock/process/QuickHistoryEntryProcess.java @@ -1,11 +1,17 @@ package com.metaweb.gridlock.process; +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONObject; + import com.metaweb.gridlock.history.HistoryEntry; import com.metaweb.gridlock.model.Project; public class QuickHistoryEntryProcess extends Process { final protected Project _project; final protected HistoryEntry _historyEntry; + boolean _done = false; public QuickHistoryEntryProcess(Project project, HistoryEntry historyEntry) { _project = project; @@ -14,19 +20,43 @@ public class QuickHistoryEntryProcess extends Process { @Override public void cancel() { + throw new RuntimeException("Not a long-running process"); } @Override public boolean isImmediate() { return true; } + + @Override + public boolean isRunning() { + throw new RuntimeException("Not a long-running process"); + } @Override public void performImmediate() { _project.history.addEntry(_historyEntry); + _done = true; } @Override - public void startPerforming() { + public void startPerforming(ProcessManager manager) { + throw new RuntimeException("Not a long-running process"); + } + + @Override + JSONObject getJSON(Properties options) throws JSONException { + JSONObject o = new JSONObject(); + + o.put("description", _historyEntry.description); + o.put("immediate", true); + o.put("status", _done ? "done" : "pending"); + + return o; + } + + @Override + public boolean isDone() { + return _done; } } diff --git a/src/main/webapp/project.html b/src/main/webapp/project.html index 67fbdef4e..8cc87d7a1 100644 --- a/src/main/webapp/project.html +++ b/src/main/webapp/project.html @@ -1 +1 @@ - Gridlock
Loading ...
\ No newline at end of file + Gridlock
Loading ...
\ No newline at end of file diff --git a/src/main/webapp/scripts/project.js b/src/main/webapp/scripts/project.js index 02259bfa2..386ec9b7c 100644 --- a/src/main/webapp/scripts/project.js +++ b/src/main/webapp/scripts/project.js @@ -48,8 +48,10 @@ function initializeUI() { ui.viewPanel = $('
').appendTo(tdLeft).css("width", tdLeft.offsetWidth + "px").css("overflow-x", "auto"); ui.facetPanel = $('
').appendTo(tdRight); ui.historyPanel = $('
').addClass("history-panel").appendTo(document.body); + ui.processPanel = $('
').addClass("process-panel").appendTo(document.body); ui.browsingEngine = new BrowsingEngine(ui.facetPanel); + ui.processWidget = new ProcessWidget(ui.processPanel); ui.historyWidget = new HistoryWidget(ui.historyPanel); ui.dataTableView = new DataTableView(ui.viewPanel); } diff --git a/src/main/webapp/scripts/project/data-table-view.js b/src/main/webapp/scripts/project/data-table-view.js index 3f4f587a9..7abb32f4d 100644 --- a/src/main/webapp/scripts/project/data-table-view.js +++ b/src/main/webapp/scripts/project/data-table-view.js @@ -289,7 +289,7 @@ DataTableView.prototype._doTextTransform = function(column, expression) { self.update(); ui.historyWidget.update(); } else { - // update process UI + ui.processWidget.update(); } }, "json" diff --git a/src/main/webapp/scripts/project/process-widget.js b/src/main/webapp/scripts/project/process-widget.js new file mode 100644 index 000000000..e7ee72e21 --- /dev/null +++ b/src/main/webapp/scripts/project/process-widget.js @@ -0,0 +1,58 @@ +function ProcessWidget(div) { + this._div = div; + this._timerID = null; + this.update(); +} + +ProcessWidget.prototype.update = function() { + if (this._timerID != null) { + return; + } + + var self = this; + Ajax.chainGetJSON( + "/command/get-processes?" + $.param({ project: theProject.id }), null, + function(data) { + self._data = data; + self._render(); + } + ); +}; + +ProcessWidget.prototype._render = function() { + var self = this; + + this._div.empty(); + + var bodyDiv = $('
').addClass("process-panel-inner").text("Testing").appendTo(this._div); + if (this._data.processes.length == 0) { + this._div.hide(); + return; + } + + this._div.show(); + + var hasPending = false; + var renderProcess = function(process) { + var div = $('
').addClass("process-panel-entry").appendTo(bodyDiv); + + if (process.status == "pending") { + div.text(process.description + " (pending)"); + } else { + div.text(process.description + " (" + process.progress + "%)"); + hasPending = true; + } + }; + + var processes = this._data.processes; + for (var i = 0; i < processes.length; i++) { + renderProcess(processes[i]); + } + + if (hasPending && this._timerID == null) { + this._timerID = window.setTimeout(function() { + self._timerID = null; + self.update(); + }, 500); + } +}; diff --git a/src/main/webapp/styles/process.css b/src/main/webapp/styles/process.css new file mode 100644 index 000000000..90923fef7 --- /dev/null +++ b/src/main/webapp/styles/process.css @@ -0,0 +1,15 @@ +.process-panel { + position: absolute; + top: -1px; + width: 100%; + text-align: center; +} + +.process-panel-inner { + border: 1px solid #ccc; + width: 500px; + padding: 10px; + background: #fffee0; + margin: 0 auto; + text-align: left; +} \ No newline at end of file