Improved grid layout CSS rules.

Started working on extending data from Freebase feature.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@289 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-03-13 07:13:18 +00:00
parent c637df71c9
commit f34577ec85
15 changed files with 647 additions and 108 deletions

View File

@ -51,6 +51,7 @@ import com.metaweb.gridworks.commands.util.GetExpressionLanguageInfoCommand;
import com.metaweb.gridworks.commands.util.GuessTypesOfColumnCommand;
import com.metaweb.gridworks.commands.util.LogExpressionCommand;
import com.metaweb.gridworks.commands.util.PreviewExpressionCommand;
import com.metaweb.gridworks.commands.util.PreviewExtendDataCommand;
import com.metaweb.gridworks.commands.util.PreviewProtographCommand;
public class GridworksServlet extends HttpServlet {
@ -106,12 +107,14 @@ public class GridworksServlet extends HttpServlet {
_commands.put("save-protograph", new SaveProtographCommand());
_commands.put("preview-expression", new PreviewExpressionCommand());
_commands.put("get-expression-language-info", new GetExpressionLanguageInfoCommand());
_commands.put("get-expression-history", new GetExpressionHistoryCommand());
_commands.put("log-expression", new LogExpressionCommand());
_commands.put("preview-expression", new PreviewExpressionCommand());
_commands.put("preview-extend-data", new PreviewExtendDataCommand());
_commands.put("preview-protograph", new PreviewProtographCommand());
_commands.put("guess-types-of-column", new GuessTypesOfColumnCommand());
}

View File

@ -0,0 +1,129 @@
package com.metaweb.gridworks.commands.util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONWriter;
import com.metaweb.gridworks.commands.Command;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.ReconCandidate;
import com.metaweb.gridworks.model.Row;
import com.metaweb.gridworks.util.FreebaseDataExtensionJob;
import com.metaweb.gridworks.util.FreebaseDataExtensionJob.DataExtension;
public class PreviewExtendDataCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
Project project = getProject(request);
String columnName = request.getParameter("columnName");
String rowIndicesString = request.getParameter("rowIndices");
if (rowIndicesString == null) {
respond(response, "{ \"code\" : \"error\", \"message\" : \"No row indices specified\" }");
return;
}
String jsonString = request.getParameter("extension");
JSONObject json = jsonStringToObject(jsonString);
JSONArray rowIndices = jsonStringToArray(rowIndicesString);
int length = rowIndices.length();
int cellIndex = project.columnModel.getColumnByName(columnName).getCellIndex();
List<String> topicNames = new ArrayList<String>();
List<String> topicGuids = new ArrayList<String>();
Set<String> guids = new HashSet<String>();
for (int i = 0; i < length; i++) {
int rowIndex = rowIndices.getInt(i);
if (rowIndex >= 0 && rowIndex < project.rows.size()) {
Row row = project.rows.get(rowIndex);
Cell cell = row.getCell(cellIndex);
if (cell != null && cell.recon != null && cell.recon.match != null) {
topicNames.add(cell.recon.match.topicName);
topicGuids.add(cell.recon.match.topicGUID);
guids.add(cell.recon.match.topicGUID);
}
}
}
FreebaseDataExtensionJob job = new FreebaseDataExtensionJob(json);
Map<String, DataExtension> map = job.extend(guids);
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "application/json");
JSONWriter writer = new JSONWriter(response.getWriter());
writer.object();
writer.key("columns");
writer.array();
for (String name : job.columnNames) {
writer.value(name);
}
writer.endArray();
writer.key("rows");
writer.array();
for (int r = 0; r < topicNames.size(); r++) {
String guid = topicGuids.get(r);
String topicName = topicNames.get(r);
if (map.containsKey(guid)) {
DataExtension ext = map.get(guid);
boolean first = true;
if (ext.data.length > 0) {
for (Object[] row : ext.data) {
writer.array();
if (first) {
writer.value(topicName);
first = false;
} else {
writer.value(null);
}
for (Object cell : row) {
if (cell != null && cell instanceof ReconCandidate) {
ReconCandidate rc = (ReconCandidate) cell;
writer.object();
writer.key("id"); writer.value(rc.topicID);
writer.key("name"); writer.value(rc.topicName);
writer.endObject();
} else {
writer.value(cell);
}
}
writer.endArray();
}
continue;
}
}
writer.array();
writer.value(topicName);
writer.endArray();
}
writer.endArray();
writer.endObject();
} catch (Exception e) {
respondException(response, e);
}
}
}

View File

@ -0,0 +1,310 @@
/**
*
*/
package com.metaweb.gridworks.util;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONWriter;
import com.metaweb.gridworks.model.ReconCandidate;
public class FreebaseDataExtensionJob {
static public class DataExtension {
final public Object[][] data;
protected DataExtension(Object[][] data) {
this.data = data;
}
}
final public JSONObject extension;
final public int columnCount;
final public List<String> columnNames = new ArrayList<String>();
public FreebaseDataExtensionJob(JSONObject obj) throws JSONException {
this.extension = obj;
this.columnCount = (obj.has("properties") && !obj.isNull("properties")) ?
countColumns(obj.getJSONArray("properties"), columnNames) : 0;
}
public Map<String, FreebaseDataExtensionJob.DataExtension> extend(Set<String> guids) throws Exception {
StringWriter writer = new StringWriter();
formulateQuery(guids, extension, writer);
URL url = new URL("http://api.freebase.com/api/service/mqlread");
InputStream is = doPost(url, "query", writer.toString());
try {
String s = ParsingUtilities.inputStreamToString(is);
JSONObject o = ParsingUtilities.evaluateJsonStringToObject(s);
Map<String, FreebaseDataExtensionJob.DataExtension> map = new HashMap<String, FreebaseDataExtensionJob.DataExtension>();
JSONArray a = o.getJSONArray("result");
int l = a.length();
for (int i = 0; i < l; i++) {
JSONObject o2 = a.getJSONObject(i);
String guid = o2.getString("guid");
FreebaseDataExtensionJob.DataExtension ext = collectResult(o2);
if (ext != null) {
map.put(guid, ext);
}
}
return map;
} finally {
is.close();
}
}
protected FreebaseDataExtensionJob.DataExtension collectResult(JSONObject obj) throws JSONException {
List<Object[]> rows = new ArrayList<Object[]>();
collectResult(rows, extension, obj, 0, 0);
Object[][] data = new Object[rows.size()][columnCount];
rows.toArray(data);
return new DataExtension(data);
}
protected void storeCell(
List<Object[]> rows,
int row,
int col,
Object value
) {
while (row >= rows.size()) {
rows.add(new Object[columnCount]);
}
rows.get(row)[col] = value;
}
protected void storeCell(
List<Object[]> rows,
int row,
int col,
JSONObject obj
) throws JSONException {
storeCell(rows, row, col,
new ReconCandidate(
obj.getString("id"),
obj.getString("guid"),
obj.getString("name"),
JSONUtilities.getStringArray(obj, "type"),
100
)
);
}
protected int[] collectResult(
List<Object[]> rows,
JSONObject extNode,
JSONObject resultNode,
int startRowIndex,
int startColumnIndex
) throws JSONException {
String propertyID = extNode.getString("id");
String expectedTypeID = extNode.getString("expected");
JSONArray a = resultNode != null && resultNode.has(propertyID) && !resultNode.isNull(propertyID) ?
resultNode.getJSONArray(propertyID) : null;
if (expectedTypeID.startsWith("/type/")) {
if (a != null) {
int l = a.length();
for (int r = 0; r < l; r++) {
Object o = a.isNull(r) ? null : a.get(r);
if (o instanceof Serializable) {
storeCell(rows, startRowIndex++, startColumnIndex, o);
}
}
}
// note that we still take up a column even if we don't have any data
return new int[] { startRowIndex, startColumnIndex + 1 };
} else {
boolean hasSubProperties = (extNode.has("properties") && !extNode.isNull("properties"));
boolean isOwnColumn = !hasSubProperties || (extNode.has("included") && extNode.getBoolean("included"));
if (a != null) {
int maxColIndex = startColumnIndex;
int l = a.length();
for (int r = 0; r < l; r++) {
JSONObject o = a.isNull(r) ? null : a.getJSONObject(r);
int startColumnIndex2 = startColumnIndex;
int startRowIndex2 = startRowIndex;
if (isOwnColumn) {
storeCell(rows, startRowIndex2++, startColumnIndex2++, o);
}
int[] rowcol = collectResult(
rows,
extNode.getJSONArray("properties"),
o,
startRowIndex,
startColumnIndex2
);
startRowIndex = rowcol[0];
maxColIndex = Math.max(maxColIndex, rowcol[1]);
}
return new int[] { startRowIndex, maxColIndex };
} else {
return new int[] {
startRowIndex,
countColumns(extNode, null)
};
}
}
}
protected int[] collectResult(
List<Object[]> rows,
JSONArray subProperties,
JSONObject resultNode,
int startRowIndex,
int startColumnIndex
) throws JSONException {
int maxStartRowIndex = startRowIndex;
int k = subProperties.length();
for (int c = 0; c < k; c++) {
int[] rowcol = collectResult(
rows,
subProperties.getJSONObject(c),
resultNode,
startRowIndex,
startColumnIndex
);
maxStartRowIndex = Math.max(maxStartRowIndex, rowcol[0]);
startColumnIndex = rowcol[1];
}
return new int[] { maxStartRowIndex, startColumnIndex };
}
static protected InputStream doPost(URL url, String name, String load) throws IOException {
URLConnection connection = url.openConnection();
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setConnectTimeout(5000);
DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
try {
String data = name + "=" + ParsingUtilities.encode(load);
dos.writeBytes(data);
} finally {
dos.flush();
dos.close();
}
connection.connect();
return connection.getInputStream();
}
static protected void formulateQuery(Set<String> guids, JSONObject node, Writer writer) throws JSONException {
JSONWriter jsonWriter = new JSONWriter(writer);
jsonWriter.object();
jsonWriter.key("query");
jsonWriter.array();
jsonWriter.object();
jsonWriter.key("guid"); jsonWriter.value(null);
jsonWriter.key("guid|=");
jsonWriter.array();
for (String guid : guids) {
jsonWriter.value(guid);
}
jsonWriter.endArray();
formulateQueryNode(node, jsonWriter);
jsonWriter.endObject();
jsonWriter.endArray();
jsonWriter.endObject();
}
static protected void formulateQueryNode(JSONObject node, JSONWriter writer) throws JSONException {
String propertyID = node.getString("id");
String expectedTypeID = node.getString("expected");
writer.key(propertyID);
writer.array();
{
if (!expectedTypeID.startsWith("/type/")) { // not literal
writer.object();
writer.key("limit"); writer.value(10);
{
boolean hasSubProperties = (node.has("properties") && !node.isNull("properties"));
if (!hasSubProperties || (node.has("included") && node.getBoolean("included"))) {
writer.key("name"); writer.value(null);
writer.key("id"); writer.value(null);
writer.key("guid"); writer.value(null);
writer.key("type"); writer.array(); writer.endArray();
}
if (hasSubProperties) {
JSONArray a = node.getJSONArray("properties");
int l = a.length();
for (int i = 0; i < l; i++) {
formulateQueryNode(a.getJSONObject(i), writer);
}
}
}
writer.endObject();
}
}
writer.endArray();
}
static protected int countColumns(JSONObject obj, List<String> columnNames) throws JSONException {
if (obj.has("properties") && !obj.isNull("properties")) {
boolean included = (obj.has("included") && obj.getBoolean("included"));
if (included && columnNames != null) {
columnNames.add(obj.getString("name"));
}
return (included ? 1 : 0) + countColumns(obj.getJSONArray("properties"), columnNames);
} else {
if (columnNames != null) {
columnNames.add(obj.getString("name"));
}
return 1;
}
}
static protected int countColumns(JSONArray a, List<String> columnNames) throws JSONException {
int c = 0;
int l = a.length();
for (int i = 0; i < l; i++) {
c += countColumns(a.getJSONObject(i), columnNames);
}
return c;
}
}

View File

@ -1 +1 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>Gridworks</title> <link rel="stylesheet" href="/styles/common.css" /> <link rel="stylesheet" href="/styles/index.css" /> <script type="text/javascript" src="externals/jquery-1.4.1.min.js"></script> <script type="text/javascript" src="externals/date.js"></script> <script type="text/javascript" src="scripts/util/string.js"></script> <script type="text/javascript" src="scripts/index.js"></script> </head> <body> <div id="header"> <a id="logo" href="http://www.metaweb.com/"><img alt="Metaweb" src="images/metaweb-headerlogo.png" /></a> </div> <div id="body"> <div id="body-empty"> <table><tr> <td id="body-empty-logo-container"><img src="images/gridworks.png" /> Gridworks</td> <td id="body-empty-create-project-panel-container"></td> </tr></table> </div> <div id="body-nonempty"> <table><tr> <td id="body-nonempty-logo-container"><img src="images/gridworks.png" /> Gridworks</td> <td id="body-nonempty-projects-container"> <div id="projects"></div> </td> <td id="body-nonempty-create-project-panel-container"></td> </tr></table> </div> </div> <div id="footer"> <a href="about.html">About Gridworks</a> &bull; &copy; 2010 <a href="http://www.metaweb.com/">Metaweb Technologies, Inc.</a> </div> <div id="body-template"> <div id="create-project-panel"> <h1>Upload Data File</h1> <form id="file-upload-form" method="post" enctype="multipart/form-data" action="/command/create-project-from-upload" accept-charset="UTF-8"> <table class="grid-layout layout-tight"> <tr><td>Data File:</td><td> <input type="file" id="project-file-input" name="project-file" /> </td></tr> <tr><td>Project Name:</td><td> <input type="text" size="30" id="project-name-input" name="project-name" /> </td></tr> <tr><td>Load up to:</td><td> <input id="limit-input" name="limit" size="5" /> data rows (optional) </td></tr> <tr><td>Skip:</td><td> <input id="skip-input" name="skip" size="5" /> initial data rows (optional) </td></tr> <tr><td></td><td> <input type="submit" value="Create Project" id="upload-file-button" /> </td></tr> </table> </form> <h1>Import Existing Project</h1> <form id="project-upload-form" method="post" enctype="multipart/form-data" action="/command/import-project" accept-charset="UTF-8"> <table id="import-project-panel-layout"> <tr><td>Project TAR File:</td><td> <input type="file" id="project-tar-file-input" name="project-file" /> </td></tr> <tr><td>Re-name Project:</td><td> <input type="text" size="30" id="project-name-input" name="project-name" /> (optional) </td></tr> <tr><td></td><td> <input type="submit" value="Import Project" id="import-project-button" /> </td></tr> </table> </form> </div> </div> </body> </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>Gridworks</title> <link rel="stylesheet" href="/styles/common.css" /> <link rel="stylesheet" href="/styles/index.css" /> <script type="text/javascript" src="externals/jquery-1.4.1.min.js"></script> <script type="text/javascript" src="externals/date.js"></script> <script type="text/javascript" src="scripts/util/string.js"></script> <script type="text/javascript" src="scripts/index.js"></script> </head> <body> <div id="header"> <a id="logo" href="http://www.metaweb.com/"><img alt="Metaweb" src="images/metaweb-headerlogo.png" /></a> </div> <div id="body"> <div id="body-empty"> <table><tr> <td id="body-empty-logo-container"><img src="images/gridworks.png" /> Gridworks</td> <td id="body-empty-create-project-panel-container"></td> </tr></table> </div> <div id="body-nonempty"> <table><tr> <td id="body-nonempty-logo-container"><img src="images/gridworks.png" /> Gridworks</td> <td id="body-nonempty-projects-container"> <div id="projects"></div> </td> <td id="body-nonempty-create-project-panel-container"></td> </tr></table> </div> </div> <div id="footer"> <a href="about.html">About Gridworks</a> &bull; &copy; 2010 <a href="http://www.metaweb.com/">Metaweb Technologies, Inc.</a> </div> <div id="body-template"> <div id="create-project-panel"> <h1>Upload Data File</h1> <form id="file-upload-form" method="post" enctype="multipart/form-data" action="/command/create-project-from-upload" accept-charset="UTF-8"> <div class="grid-layout layout-tight"><table> <tr><td>Data File:</td><td> <input type="file" id="project-file-input" name="project-file" /> </td></tr> <tr><td>Project Name:</td><td> <input type="text" size="30" id="project-name-input" name="project-name" /> </td></tr> <tr><td>Load up to:</td><td> <input id="limit-input" name="limit" size="5" /> data rows (optional) </td></tr> <tr><td>Skip:</td><td> <input id="skip-input" name="skip" size="5" /> initial data rows (optional) </td></tr> <tr><td></td><td> <input type="submit" value="Create Project" id="upload-file-button" /> </td></tr> </table></div> </form> <h1>Import Existing Project</h1> <form id="project-upload-form" method="post" enctype="multipart/form-data" action="/command/import-project" accept-charset="UTF-8"> <table id="import-project-panel-layout"> <tr><td>Project TAR File:</td><td> <input type="file" id="project-tar-file-input" name="project-file" /> </td></tr> <tr><td>Re-name Project:</td><td> <input type="text" size="30" id="project-name-input" name="project-name" /> (optional) </td></tr> <tr><td></td><td> <input type="submit" value="Import Project" id="import-project-button" /> </td></tr> </table> </form> </div> </div> </body> </html>

View File

@ -1 +1 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Gridworks</title> <link type="text/css" rel="stylesheet" href="externals/suggest/css/suggest-1.0.3.min.css" /> <link type="text/css" rel="stylesheet" href="externals/jquery-ui/css/ui-lightness/jquery-ui-1.7.2.custom.css" /> <link rel="stylesheet" href="/styles/common.css" /> <link rel="stylesheet" href="/styles/util/menu.css" /> <link rel="stylesheet" href="/styles/util/dialog.css" /> <link rel="stylesheet" href="/styles/util/custom-suggest.css" /> <link rel="stylesheet" href="/styles/project.css" /> <link rel="stylesheet" href="/styles/project/history.css" /> <link rel="stylesheet" href="/styles/project/browsing.css" /> <link rel="stylesheet" href="/styles/project/process.css" /> <link rel="stylesheet" href="/styles/project/menu-bar.css" /> <link rel="stylesheet" href="/styles/views/data-table-view.css" /> <link rel="stylesheet" href="/styles/dialogs/expression-preview-dialog.css" /> <link rel="stylesheet" href="/styles/dialogs/recon-dialog.css" /> <link rel="stylesheet" href="/styles/dialogs/facet-based-edit-dialog.css" /> <link rel="stylesheet" href="/styles/protograph/schema-alignment-dialog.css" /> <script type="text/javascript" src="externals/jquery-1.4.1.min.js"></script> <script type="text/javascript" src="externals/suggest/suggest-1.0.3.min.js"></script> <script type="text/javascript" src="externals/jquery-ui/jquery-ui-1.7.2.custom.min.js"></script> <script type="text/javascript" src="externals/date.js"></script> <script type="text/javascript" src="scripts/util/misc.js"></script> <script type="text/javascript" src="scripts/util/url.js"></script> <script type="text/javascript" src="scripts/util/string.js"></script> <script type="text/javascript" src="scripts/util/ajax.js"></script> <script type="text/javascript" src="scripts/util/menu.js"></script> <script type="text/javascript" src="scripts/util/dialog.js"></script> <script type="text/javascript" src="scripts/util/dom.js"></script> <script type="text/javascript" src="scripts/util/custom-suggest.js"></script> <script type="text/javascript" src="scripts/project.js"></script> <script type="text/javascript" src="scripts/project/history-widget.js"></script> <script type="text/javascript" src="scripts/project/process-widget.js"></script> <script type="text/javascript" src="scripts/project/menu-bar.js"></script> <script type="text/javascript" src="scripts/project/browsing-engine.js"></script> <script type="text/javascript" src="scripts/project/scripting.js"></script> <script type="text/javascript" src="scripts/facets/list-facet.js"></script> <script type="text/javascript" src="scripts/facets/range-facet.js"></script> <script type="text/javascript" src="scripts/facets/text-search-facet.js"></script> <script type="text/javascript" src="scripts/views/data-table-view.js"></script> <script type="text/javascript" src="scripts/views/data-table-cell-ui.js"></script> <script type="text/javascript" src="scripts/views/data-table-column-header-ui.js"></script> <script type="text/javascript" src="scripts/dialogs/recon-dialog.js"></script> <script type="text/javascript" src="scripts/dialogs/expression-preview-dialog.js"></script> <script type="text/javascript" src="scripts/dialogs/facet-based-edit-dialog.js"></script> <script type="text/javascript" src="scripts/protograph/schema-alignment.js"></script> <script type="text/javascript" src="scripts/protograph/schema-alignment-ui-node.js"></script> <script type="text/javascript" src="scripts/protograph/schema-alignment-ui-link.js"></script> </head> <body> <div id="header"> <a id="logo" href="http://www.metaweb.com/"><img alt="Metaweb" src="images/metaweb-headerlogo.png" /></a> <div id="path"><a class="app-path-section" href="./index.html">Gridworks</a> &raquo; </div> </div> <div id="body"> <div id="loading-message"><img src="images/large-spinner.gif" /> starting up ...</div> </div> <div id="footer"> <a href="about.html">About Gridworks</a> &bull; &copy; 2010 <a href="http://www.metaweb.com/">Metaweb Technologies, Inc.</a> </div> </body> </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Gridworks</title> <link type="text/css" rel="stylesheet" href="externals/suggest/css/suggest-1.0.3.min.css" /> <link type="text/css" rel="stylesheet" href="externals/jquery-ui/css/ui-lightness/jquery-ui-1.7.2.custom.css" /> <link rel="stylesheet" href="/styles/common.css" /> <link rel="stylesheet" href="/styles/util/menu.css" /> <link rel="stylesheet" href="/styles/util/dialog.css" /> <link rel="stylesheet" href="/styles/util/custom-suggest.css" /> <link rel="stylesheet" href="/styles/project.css" /> <link rel="stylesheet" href="/styles/project/history.css" /> <link rel="stylesheet" href="/styles/project/browsing.css" /> <link rel="stylesheet" href="/styles/project/process.css" /> <link rel="stylesheet" href="/styles/project/menu-bar.css" /> <link rel="stylesheet" href="/styles/views/data-table-view.css" /> <link rel="stylesheet" href="/styles/dialogs/expression-preview-dialog.css" /> <link rel="stylesheet" href="/styles/dialogs/recon-dialog.css" /> <link rel="stylesheet" href="/styles/dialogs/facet-based-edit-dialog.css" /> <link rel="stylesheet" href="/styles/dialogs/extend-data-preview-dialog.css" /> <link rel="stylesheet" href="/styles/protograph/schema-alignment-dialog.css" /> <script type="text/javascript" src="externals/jquery-1.4.1.min.js"></script> <script type="text/javascript" src="externals/suggest/suggest-1.0.3.min.js"></script> <script type="text/javascript" src="externals/jquery-ui/jquery-ui-1.7.2.custom.min.js"></script> <script type="text/javascript" src="externals/date.js"></script> <script type="text/javascript" src="scripts/util/misc.js"></script> <script type="text/javascript" src="scripts/util/url.js"></script> <script type="text/javascript" src="scripts/util/string.js"></script> <script type="text/javascript" src="scripts/util/ajax.js"></script> <script type="text/javascript" src="scripts/util/menu.js"></script> <script type="text/javascript" src="scripts/util/dialog.js"></script> <script type="text/javascript" src="scripts/util/dom.js"></script> <script type="text/javascript" src="scripts/util/custom-suggest.js"></script> <script type="text/javascript" src="scripts/project.js"></script> <script type="text/javascript" src="scripts/project/history-widget.js"></script> <script type="text/javascript" src="scripts/project/process-widget.js"></script> <script type="text/javascript" src="scripts/project/menu-bar.js"></script> <script type="text/javascript" src="scripts/project/browsing-engine.js"></script> <script type="text/javascript" src="scripts/project/scripting.js"></script> <script type="text/javascript" src="scripts/facets/list-facet.js"></script> <script type="text/javascript" src="scripts/facets/range-facet.js"></script> <script type="text/javascript" src="scripts/facets/text-search-facet.js"></script> <script type="text/javascript" src="scripts/views/data-table-view.js"></script> <script type="text/javascript" src="scripts/views/data-table-cell-ui.js"></script> <script type="text/javascript" src="scripts/views/data-table-column-header-ui.js"></script> <script type="text/javascript" src="scripts/dialogs/recon-dialog.js"></script> <script type="text/javascript" src="scripts/dialogs/expression-preview-dialog.js"></script> <script type="text/javascript" src="scripts/dialogs/facet-based-edit-dialog.js"></script> <script type="text/javascript" src="scripts/dialogs/extend-data-preview-dialog.js"></script> <script type="text/javascript" src="scripts/protograph/schema-alignment.js"></script> <script type="text/javascript" src="scripts/protograph/schema-alignment-ui-node.js"></script> <script type="text/javascript" src="scripts/protograph/schema-alignment-ui-link.js"></script> </head> <body> <div id="header"> <a id="logo" href="http://www.metaweb.com/"><img alt="Metaweb" src="images/metaweb-headerlogo.png" /></a> <div id="path"><a class="app-path-section" href="./index.html">Gridworks</a> &raquo; </div> </div> <div id="body"> <div id="loading-message"><img src="images/large-spinner.gif" /> starting up ...</div> </div> <div id="footer"> <a href="about.html">About Gridworks</a> &bull; &copy; 2010 <a href="http://www.metaweb.com/">Metaweb Technologies, Inc.</a> </div> </body> </html>

View File

@ -32,13 +32,13 @@ function ExpressionPreviewDialog(title, cellIndex, rowIndices, values, expressio
};
ExpressionPreviewDialog.generateWidgetHtml = function() {
return '<table class="grid-layout layout-tighter layout-full" rows="4" cols="2">' +
return '<div class="grid-layout layout-tight layout-full"><table rows="4" cols="2">' +
'<tr>' +
'<td>Expression</td>' +
'<td>Language</td>' +
'</tr>' +
'<tr>' +
'<td rowspan="2"><textarea class="expression-preview-code" bind="expressionPreviewTextarea" /></td>' +
'<td rowspan="2"><div class="input-container"><textarea class="expression-preview-code" bind="expressionPreviewTextarea" /></div></td>' +
'<td width="150" height="1">' +
'<select bind="expressionPreviewLanguageSelect">' +
'<option value="gel">Native expression language</option>' +
@ -70,7 +70,7 @@ ExpressionPreviewDialog.generateWidgetHtml = function() {
'</div>' +
'</td>' +
'</tr>' +
'</table>';
'</table></div>';
};
ExpressionPreviewDialog.Widget = function(

View File

@ -0,0 +1,45 @@
function ExtendDataPreviewDialog(columnName, rowIndices, onDone) {
this._onDone = onDone;
var self = this;
var frame = DialogSystem.createDialog();
frame.width("900px").addClass("extend-data-preview-dialog");
var header = $('<div></div>').addClass("dialog-header").text("Add Columns from Freebase Based on Column " + columnName).appendTo(frame);
var body = $('<div></div>').addClass("dialog-body").appendTo(frame);
var footer = $('<div></div>').addClass("dialog-footer").appendTo(frame);
var html = $(
'<div class="grid-layout layout-normal layout-full"><table style="height: 600px">' +
'<tr>' +
'<td width="150" height="1">Add Property</td>' +
'<td height="100%" rowspan="4"><div class="preview-container" bind="previewDiv"></div></td>' +
'</tr>' +
'<tr>' +
'<td height="1"><div class="input-container"><input /></div></td>' +
'</tr>' +
'<tr>' +
'<td height="1">Suggested Properties</td>' +
'</tr>' +
'<tr>' +
'<td height="99%"><div class="suggested-property-container"></div></td>' +
'</tr>' +
'</table></div>'
).appendTo(body);
this._elmts = DOM.bind(html);
$('<button></button>').html("&nbsp;&nbsp;OK&nbsp;&nbsp;").click(function() {
DialogSystem.dismissUntil(self._level - 1);
self._onDone(self._previewWidget.getExpression(true));
}).appendTo(footer);
$('<button></button>').text("Cancel").click(function() {
DialogSystem.dismissUntil(self._level - 1);
}).appendTo(footer);
this._level = DialogSystem.showDialog(frame);
};
ExtendDataPreviewDialog.prototype.update = function() {
};

View File

@ -39,7 +39,7 @@ ReconDialog.prototype._createDialog = function() {
'<li><a href="#recon-dialog-tabs-strict">Strict</a></li>' +
'</ul>' +
'<div id="recon-dialog-tabs-heuristic">' +
'<table class="grid-layout layout-normal layout-full">' +
'<div class="grid-layout layout-normal layout-full"><table>' +
'<tr>' +
'<td>Reconcile each cell to a Freebase topic of type:</td>' +
'<td>Also use relevant details from other columns:</td>' +
@ -69,17 +69,17 @@ ReconDialog.prototype._createDialog = function() {
'<input type="radio" name="recon-dialog-heuristic-service" value="relevance" /> relevance service ' +
'</td>' +
'</tr>' +
'</table>' +
'</table></div>' +
'</div>' +
'<div id="recon-dialog-tabs-strict" style="display: none;">' +
'<p>Each cell contains:</p>' +
'<table class="grid-layout layout-normal layout-full">' +
'<div class="grid-layout layout-normal layout-full"><table>' +
'<tr><td width="1%"><input type="radio" name="recon-dialog-strict-choice" value="id" checked /></td><td>a Freebase ID, e.g., /en/solar_system</td></tr>' +
'<tr><td><input type="radio" name="recon-dialog-strict-choice" value="guid" /></td><td>a Freebase GUID, e.g., #9202a8c04000641f80000000000354ae</td></tr>' +
'<tr>' +
'<td width="1%"><input type="radio" name="recon-dialog-strict-choice" value="key" /></td>' +
'<td>' +
'<table class="grid-layout layout-tighter layout-full">' +
'<div class="grid-layout layout-tighter layout-full"><table>' +
'<tr><td colspan="2">a Freebase key in</td></tr>' +
'<tr>' +
'<td width="1%"><input type="radio" name="recon-dialog-strict-namespace-choice" value="/wikipedia/en" nsName="Wikipedia EN" checked /></td>' +
@ -89,10 +89,10 @@ ReconDialog.prototype._createDialog = function() {
'<td width="1%"><input type="radio" name="recon-dialog-strict-namespace-choice" value="other" /></td>' +
'<td>this namespace: <input bind="strictNamespaceInput" /></td>' +
'</tr>' +
'</table>' +
'</table></div>' +
'</td>' +
'</tr>' +
'</table>' +
'</table></div>' +
'</div>' +
'</div>'
).appendTo(body);
@ -117,7 +117,8 @@ ReconDialog.prototype._populateDialog = function() {
/*
* Populate types in heuristic tab
*/
var typeTable = $('<table></table>').addClass("grid-layout layout-tighter").appendTo(this._elmts.heuristicTypeContainer)[0];
var typeTableContainer = $('<div>').addClass("grid-layout layout-tighter").appendTo(this._elmts.heuristicTypeContainer);
var typeTable = $('<table></table>').appendTo(typeTableContainer)[0];
var createTypeChoice = function(type, check) {
var tr = typeTable.insertRow(typeTable.rows.length);
var td0 = tr.insertCell(0);
@ -145,11 +146,15 @@ ReconDialog.prototype._populateDialog = function() {
/*
* Populate properties in heuristic tab
*/
var heuristicDetailTableContainer = $('<div>')
.addClass("grid-layout layout-tighter")
.appendTo(this._elmts.heuristicDetailContainer);
var heuristicDetailTable = $(
'<table>' +
'<tr><th>Column</th><th>Freebase property</th></tr>' +
'</table>'
).addClass("grid-layout layout-tighter").appendTo(this._elmts.heuristicDetailContainer)[0];
).appendTo(heuristicDetailTableContainer)[0];
function renderDetailColumn(column) {
var tr = heuristicDetailTable.insertRow(heuristicDetailTable.rows.length);

View File

@ -124,22 +124,26 @@ HistoryWidget.prototype._showExtractOperationsDialog = function(json) {
var body = $('<div></div>').addClass("dialog-body").appendTo(frame);
var footer = $('<div></div>').addClass("dialog-footer").appendTo(frame);
$('<p></p>').text(
"The following JSON code encodes the operations you have done that can be abstracted. " +
"You can copy and save it in order to apply the same operations in the future.").appendTo(body);
var html = $(
'<div class="grid-layout layout-normal layout-full"><table>' +
'<tr><td colspan="2">' +
'The following JSON code encodes the operations you have done that can be abstracted. ' +
'You can copy and save it in order to apply the same operations in the future.' +
'</td></tr>' +
'<tr>' +
'<td width="50%">' +
'<div class="extract-operation-dialog-entries"><table cellspacing="5" bind="entryTable"></table></div>' +
'</td>' +
'<td width="50%">' +
'<div class="input-container"><textarea wrap="off" class="history-operation-json" bind="textarea" /></div>' +
'</td>' +
'</tr>' +
'</table></div>'
).appendTo(body);
var elmts = DOM.bind(html);
var table = $('<table width="100%" cellspacing="0" cellpadding="0"><tr></tr></table>')
.addClass("extract-operation-dialog-layout")
.appendTo(body)[0];
var leftColumn = table.rows[0].insertCell(0);
var rightColumn = table.rows[0].insertCell(1);
$(leftColumn).width("50%");
$(rightColumn).width("50%").css("padding-left", "20px");
var entryDiv = $('<div>').addClass("extract-operation-dialog-entries").appendTo(leftColumn);
var entryTable = $('<table cellspacing="5"></table>').appendTo(entryDiv)[0];
var entryTable = elmts.entryTable[0];
var createEntry = function(entry) {
var tr = entryTable.insertRow(entryTable.rows.length);
var td0 = tr.insertCell(0);
@ -162,11 +166,7 @@ HistoryWidget.prototype._showExtractOperationsDialog = function(json) {
for (var i = 0; i < json.entries.length; i++) {
createEntry(json.entries[i]);
}
var textarea = $('<textarea />')
.attr("wrap", "off")
.addClass("extract-operation-dialog-textarea")
.appendTo(rightColumn);
var updateJson = function() {
var a = [];
for (var i = 0; i < json.entries.length; i++) {
@ -175,7 +175,7 @@ HistoryWidget.prototype._showExtractOperationsDialog = function(json) {
a.push(entry.operation);
}
}
textarea.text(JSON.stringify(a, null, 2));
elmts.textarea.text(JSON.stringify(a, null, 2));
};
updateJson();
@ -188,7 +188,7 @@ HistoryWidget.prototype._showExtractOperationsDialog = function(json) {
textarea[0].select();
};
HistoryWidget.prototype._showApplyOperationsDialog = function(json) {
HistoryWidget.prototype._showApplyOperationsDialog = function() {
var self = this;
var frame = DialogSystem.createDialog();
frame.width("800px");
@ -197,21 +197,22 @@ HistoryWidget.prototype._showApplyOperationsDialog = function(json) {
var body = $('<div></div>').addClass("dialog-body").appendTo(frame);
var footer = $('<div></div>').addClass("dialog-footer").appendTo(frame);
$('<p></p>').text(
"Paste the JSON code encoding the operations to perform.").appendTo(body);
var textarea = $('<textarea />')
.attr("wrap", "off")
.css("white-space", "pre")
.css("font-family", "monospace")
.width("100%")
.height("400px")
.appendTo(body);
textarea.text(JSON.stringify(json, null, 2));
var html = $(
'<div class="grid-layout layout-normal layout-full"><table>' +
'<tr><td>' +
'Paste the JSON code encoding the operations to perform.' +
'</td></tr>' +
'<tr><td>' +
'<div class="input-container"><textarea wrap="off" bind="textarea" class="history-operation-json" /></div>' +
'</td></tr>' +
'</table></div>'
).appendTo(body);
var elmts = DOM.bind(html);
$('<button></button>').text("Apply").click(function() {
try {
var json = JSON.parse(textarea[0].value);
var json = JSON.parse(elmts.textarea[0].value);
} catch (e) {
alert("The JSON you pasted is invalid.");
return;

View File

@ -274,6 +274,11 @@ DataTableColumnHeaderUI.prototype._createMenuForColumnHeader = function(elmt) {
label: "Add Column Based on This Column ...",
click: function() { self._doAddColumn("value"); }
},
{
label: "Add Columns From Freebase ...",
click: function() { self._doAddColumnFromFreebase(); }
},
{},
{
label: "Remove This Column",
click: function() { self._doRemoveColumn(); }
@ -547,7 +552,7 @@ DataTableColumnHeaderUI.prototype._doTextTransformPrompt = function() {
var footer = $('<div></div>').addClass("dialog-footer").appendTo(frame);
body.html(
'<table class="expression-preview-layout">' +
'<div class="grid-layout layout-tight layout-full"><table>' +
'<tr>' +
'<td colspan="4">' + ExpressionPreviewDialog.generateWidgetHtml() + '</td>' +
'</tr>' +
@ -743,7 +748,7 @@ DataTableColumnHeaderUI.prototype._doAddColumn = function(initialExpression) {
var footer = $('<div></div>').addClass("dialog-footer").appendTo(frame);
body.html(
'<table class="expression-preview-layout" cols="2">' +
'<div class="grid-layout layout-normal layout-full"><table cols="2">' +
'<tr>' +
'<td width="1%" style="white-space: pre;">' +
'New column name' +
@ -765,7 +770,7 @@ DataTableColumnHeaderUI.prototype._doAddColumn = function(initialExpression) {
'<tr>' +
'<td colspan="2">' + ExpressionPreviewDialog.generateWidgetHtml() + '</td>' +
'</tr>' +
'</table>'
'</table></div>'
);
var bodyElmts = DOM.bind(body);
@ -813,6 +818,16 @@ DataTableColumnHeaderUI.prototype._doAddColumn = function(initialExpression) {
);
};
DataTableColumnHeaderUI.prototype._doAddColumnFromFreebase = function() {
var o = DataTableView.sampleVisibleRows(this._column);
new ExtendDataPreviewDialog(
this._column.name,
o.rowIndices,
function() {}
);
};
DataTableColumnHeaderUI.prototype._doRemoveColumn = function() {
Gridworks.postProcess(
"remove-column",

View File

@ -18,52 +18,74 @@ tr, td {
vertical-align: baseline;
}
table.grid-layout {
border-collapse: collapse;
div.grid-layout > table {
border-collapse: separate;
}
table.grid-layout.layout-full {
div.grid-layout.layout-full table {
width: 100%;
}
table.grid-layout > tbody > tr > th {
div.grid-layout > table > tbody > tr > th, div.grid-layout > table > tbody > tr > td {
padding: 0px;
text-align: left;
vertical-align: baseline;
vertical-align: top;
}
table.grid-layout > tbody > tr > td {
vertical-align: baseline;
div.grid-layout.layout-normal {
margin: -10px;
}
table.grid-layout > tbody > tr > td:last-child {
border-right: 0px;
}
table.grid-layout > tbody > tr:last-child > td {
border-bottom: 0px;
}
table.grid-layout.layout-normal > tbody > tr > td {
padding-right: 10px;
padding-bottom: 10px;
}
table.grid-layout.layout-tight > tbody > tr > td {
padding-right: 7px;
padding-bottom: 7px;
}
table.grid-layout.layout-tighter > tbody > tr > td {
padding-right: 5px;
padding-bottom: 5px;
}
table.grid-layout.layout-tightest > tbody > tr > td {
padding-right: 3px;
padding-bottom: 3px;
}
table.grid-layout.layout-loose > tbody > tr > td {
padding-right: 15px;
padding-bottom: 15px;
}
table.grid-layout.layout-looser > tbody > tr > td {
padding-right: 20px;
padding-bottom: 20px;
div.grid-layout.layout-normal > table {
border-spacing: 10px;
}
input {
vertical-align: middle;
div.grid-layout.layout-tight {
margin: -7px;
}
div.grid-layout.layout-tight > table {
border-spacing: 7px;
}
div.grid-layout.layout-tighter {
margin: -5px;
}
div.grid-layout.layout-tighter > table {
border-spacing: 5px;
}
div.grid-layout.layout-tightest {
margin: -3px;
}
div.grid-layout.layout-tightest > table {
border-spacing: 3px;
}
div.grid-layout.layout-loose {
margin: -15px;
}
div.grid-layout.layout-loose > table {
border-spacing: 15px;
}
div.grid-layout.layout-looser {
margin: -20px;
}
div.grid-layout.layout-looser > table {
border-spacing: 20px;
}
input[type="checkbox"], input[type="radio"] {
vertical-align: baseline;
}
div.input-container {
padding: 3px;
}
div.input-container > input, div.input-container > textarea {
display: block;
width: 100%;
padding: 2px;
border: 1px inset;
margin-left: -3px;
margin-top: -3px;
vertical-align: top;
}
a.action {

View File

@ -1,14 +1,10 @@
.expression-preview-layout .ui-tabs .ui-tabs-nav li a {
#expression-preview-tabs .ui-tabs-nav li a {
padding: 0.15em 1em;
}
textarea.expression-preview-code {
font-family: monospace;
height: 5em;
margin: 0;
padding: 2%;
width: 96%;
border: 1px solid #aaa;
vertical-align: top;
}

View File

@ -0,0 +1,19 @@
.extend-data-preview-dialog .suggested-property-container {
border: 1px solid #aaa;
padding: 10px;
overflow: auto;
height: 100%;
}
.extend-data-preview-dialog input.property-suggest {
display: block;
padding: 2%;
width: 96%;
}
.extend-data-preview-dialog .preview-container {
border: 1px solid #aaa;
padding: 10px;
overflow: auto;
height: 100%;
}

View File

@ -86,20 +86,14 @@ a.history-entry:hover {
text-decoration: none;
}
table.extract-operation-dialog-layout td {
vertical-align: top;
}
.extract-operation-dialog-entries {
height: 400px;
padding: 2px;
border: 1px inset;
overflow: auto;
}
textarea.extract-operation-dialog-textarea {
display: block;
margin: 0;
vertical-align: top;
textarea.history-operation-json {
white-space: pre;
font-family: monospace;
padding: 2%;
width: 96%;
height: 400px;
}

View File

@ -75,18 +75,18 @@
<body><div style="width: 800px; margin: auto;">
<h1>Gridworks</h1>
<p>Requires: Java 6. Data formats supported: TSV, CSV, Excel.</p>
<p>Requires: Java 6. Data formats supported: TSV, CSV, Excel. Last updated: Fri 3/12/2010 12:17pm PST</p>
<table id="downloads">
<tr>
<td><div>
<h2>Mac OSX</h2>
<center>
<a class="download-link" href="gridworks-1.0b.dmg"><table class="download-card">
<a class="download-link" href="gridworks-1.0b2-r91435.dmg"><table class="download-card">
<tr>
<td width="1%"><img src="gridworks.png" /></td>
<td>
<h3>Gridworks-1.0b.dmg</h3>
<h3>Gridworks-1.0b2-r91435.dmg</h3>
<div class="instructions">Download, open, then drag<br/>icon to Applications folder</div>
</td>
</tr>
@ -96,11 +96,11 @@
<td><div>
<h2>Windows</h2>
<center>
<a class="download-link" href="gridworks-1.0b.zip"><table class="download-card">
<a class="download-link" href="gridworks-1.0b2-r91435.zip"><table class="download-card">
<tr>
<td width="1%"><img src="gridworks.png" /></td>
<td>
<h3>Gridworks-1.0b.zip</h3>
<h3>Gridworks-1.0b2-r91435.zip</h3>
<div class="instructions">Download, unzip, and<br/>run .exe inside</div>
</td>
</tr>