Refactoring
diff --git a/asterix-examples/src/main/resources/black-cherry/cherry.tpl b/asterix-examples/src/main/resources/black-cherry/cherry.tpl
index 778043e..af3220f 100755
--- a/asterix-examples/src/main/resources/black-cherry/cherry.tpl
+++ b/asterix-examples/src/main/resources/black-cherry/cherry.tpl
@@ -201,10 +201,6 @@
<div class="modal fade" id="drilldown_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
- <h4 class="modal-title">Explore Tweets</h4>
- </div>
<div class="modal-body" id="drilldown_modal_body">
<!-- Tweet -->
diff --git a/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js b/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
index a6b3978..cd467ce 100755
--- a/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
+++ b/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
@@ -1,7 +1,25 @@
+/**
+* Asterix SDK - Beta Version
+* @author Eugenia Gabrielov <genia.likes.science@gmail.com>
+*
+* This is a Javascript helper file for generating AQL queries for AsterixDB (https://code.google.com/p/asterixdb/)
+*/
+
+/**
+* AsterixDBConnection
+*
+* This is a handler for connections to a local AsterixDB REST API Endpoint.
+* This initialization takes as input a configuraiton object, and initializes
+* same basic functionality.
+*/
function AsterixDBConnection(configuration) {
this._properties = {};
this._properties["dataverse"] = "";
this._properties["mode"] = "synchronous";
+ this._properties["error"] = function (e) {
+ // TODO Needs to be more informative
+ //alert(e);
+ }
// This is a demo setup related fix. Enabled by proxy to Asterix REST API.
this._properties["endpoint_root"] = "/";
@@ -16,6 +34,11 @@
}
+/**
+* dataverse
+*
+* Sets dataverse for execution for the AsterixDBConnection.
+*/
AsterixDBConnection.prototype.dataverse = function(dataverseName) {
this._properties["dataverse"] = dataverseName;
@@ -23,6 +46,14 @@
};
+/**
+* query (http://asterix.ics.uci.edu/documentation/api.html#QueryApi)
+*
+* @param statements, statements of an AQL query
+* @param successFn, a function to execute if this query is run successfully
+* @param mode, a string either "synchronous" or "asynchronous", depending on preferred
+* execution mode.
+*/
AsterixDBConnection.prototype.query = function(statements, successFn, mode) {
if ( typeof statements === 'string') {
@@ -30,7 +61,7 @@
}
var m = typeof mode ? mode : "synchronous";
-
+
var query = "use dataverse " + this._properties["dataverse"] + ";\n" + statements.join("\n");
this._api(
@@ -45,11 +76,17 @@
return this;
};
-
-AsterixDBConnection.prototype.query_status = function(data, successFn) {
-
+/**
+* query_status (http://asterix.ics.uci.edu/documentation/api.html#QueryStatusApi)
+*
+* @param handle, a json object of the form {"handle" : handleObject}, where
+* the handle object is an opaque handle previously returned
+* from an asynchronous call.
+* @param successFn, a function to call on successful execution of this API call.
+*/
+AsterixDBConnection.prototype.query_status = function(handle, successFn) {
this._api(
- data,
+ handle,
successFn,
"query/status"
);
@@ -58,9 +95,17 @@
};
-AsterixDBConnection.prototype.query_result = function(data, successFn) {
+/**
+* query_result (http://asterix.ics.uci.edu/documentation/api.html#AsynchronousResultApi)
+*
+* handle, a json object of the form {"handle" : handleObject}, where
+* the handle object is an opaque handle previously returned
+* from an asynchronous call.
+* successFn, a function to call on successful execution of this API call.
+*/
+AsterixDBConnection.prototype.query_result = function(handle, successFn) {
this._api(
- data,
+ handle,
successFn,
"query/result"
);
@@ -69,6 +114,12 @@
};
+/**
+* ddl (http://asterix.ics.uci.edu/documentation/api.html#DdlApi)
+*
+* @param statements, statements to run through ddl api
+* @param successFn, a function to execute if they are successful
+*/
AsterixDBConnection.prototype.ddl = function(statements, successFn) {
if ( typeof statements === 'string') {
statements = [ statements ];
@@ -84,6 +135,15 @@
}
+/**
+* update (http://asterix.ics.uci.edu/documentation/api.html#UpdateApi)
+*
+* @param statements, statement(s) for an update API call
+* @param successFn, a function to run if this is executed successfully.
+*
+* This is an AsterixDBConnection handler for the update API. It passes statements provided
+* to the internal API endpoint handler.
+*/
AsterixDBConnection.prototype.update = function(statements, successFn) {
if ( typeof statements === 'string') {
statements = [ statements ];
@@ -99,8 +159,51 @@
}
+/**
+* meta
+* @param statements, a string or a list of strings representing an Asterix query object
+* @param successFn, a function to execute if call succeeds
+*
+* Queries without a dataverse. This is a work-around for an Asterix REST API behavior
+* that sometiems throws an error. This is handy for Asterix Metadata queries.
+*/
+AsterixDBConnection.prototype.meta = function(statements, successFn) {
+
+ if ( typeof statements === 'string') {
+ statements = [ statements ];
+ }
+
+ var query = statements.join("\n");
+
+ this._api(
+ {
+ "query" : query,
+ "mode" : "synchronous"
+ },
+ successFn,
+ "query"
+ );
+
+ return this;
+}
+
+
+/**
+* _api
+*
+* @param json, the data to be passed with the request
+* @param onSuccess, the success function to be run if this succeeds
+* @param endpoint, a string representing one of the Asterix API endpoints
+*
+* Documentation of endpoints is here:
+* http://asterix.ics.uci.edu/documentation/api.html
+*
+* This is treated as an internal method for making the actual call to the API.
+* TODO Fix jquery dependency
+*/
AsterixDBConnection.prototype._api = function(json, onSuccess, endpoint) {
var success_fn = onSuccess;
+ var error_fn = this._properties["error"];
var endpoint_url = this._properties["endpoint_root"] + endpoint;
$.ajax({
@@ -112,12 +215,14 @@
success_fn(data);
},
error: function(xhr, status, error) {
+ error_fn(error);
}
});
return this;
};
+// TODO Better documentation below here.
// Asterix Expressions - Base
function AExpression () {
diff --git a/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js b/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
index 7918648..6268758 100755
--- a/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
+++ b/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
@@ -240,7 +240,7 @@
"data" : formData
};
- // TODO
+ // TODO Make dialog work correctly.
//$('#dialog').html(APIqueryTracker["query"]);
if (build_cherry_mode == "synchronous") {
@@ -297,11 +297,6 @@
/**
-*
-*/
-
-
-/**
* getAllDataverseTweetbooks
*
* no params
@@ -309,31 +304,38 @@
* Returns all datasets of type TweetbookEntry, populates review_mode_tweetbooks
*/
function getAllDataverseTweetbooks(fn_tweetbooks) {
- // Tweetbook Metadata Query
- var getTweetbooks = new FLWOGRExpression()
+
+ // This creates a query to the Metadata for datasets of type
+ // TweetBookEntry. Note that if we throw in a WhereClause (commented out below)
+ // there is an odd error. This is being fixed and will be removed from this demo.
+ var getTweetbooksQuery = new FLWOGRExpression()
.ForClause("$ds", new AExpression("dataset Metadata.Dataset"))
- .WhereClause(new AExpression('$ds.DataTypeName = "TweetbookEntry"'))
+ //.WhereClause(new AExpression('$ds.DataTypeName = "TweetbookEntry"'))
.ReturnClause({
- "tweetbookTitle" : "$ds.DatasetName",
+ "DataTypeName" : "$ds.DataTypeName",
+ "DatasetName" : "$ds.DatasetName"
});
- // Run query
- A.query(getTweetbooks.val(), function(r) {
+ // Now create a function that will be called when tweetbooks succeed.
+ // In this case, we want to parse out the results object from the Asterix
+ // REST API response.
+ var tweetbooksSuccess = function(r) {
// Parse tweetbook metadata results
- var tweetbookMetadata = r["results"];
-
- // Add existing tweetbooks
- review_mode_tweetbooks = [];
- $.each(tweetbookMetadata, function (i, v) {
- review_mode_tweetbooks.push(tweetbookMetadata[i].split(": \"")[1].split("\"")[0]);
+ $.each(r.results, function(i, data) {
+ if ($.parseJSON(data)["DataTypeName"] == "TweetbookEntry") {
+ review_mode_tweetbooks.push($.parseJSON(data)["DatasetName"]);
+ }
});
- // Populate review screen, if possible.
+ // Now, if any tweetbooks already exist, opulate review screen.
$('#review-tweetbook-titles').html('');
- for (tb in review_mode_tweetbooks) {
- addTweetBookDropdownItem(review_mode_tweetbooks[tb]);
- }
- });
+ $.each(review_mode_tweetbooks, function(i, tweetbook) {
+ addTweetBookDropdownItem(tweetbook);
+ });
+ };
+
+ // Now, we are ready to run a query.
+ A.meta(getTweetbooksQuery.val(), tweetbooksSuccess);
}
@@ -468,48 +470,65 @@
*
* { "cell": { rectangle: [{ point: [22.5, 64.5]}, { point: [24.5, 66.5]}]}, "count": { int64: 5 }}
*/
-function getRecord(cell_count_record) {
- // This is a really hacky way to pull out the digits, but it works for now.
- var values = cell_count_record.replace("int64","").match(/[-+]?[0-9]*\.?[0-9]+/g);
- var record_representation = {};
-
- record_representation["latSW"] = parseFloat(values[0]);
- record_representation["lngSW"] = parseFloat(values[1]);
- record_representation["latNE"] = parseFloat(values[2]);
- record_representation["lngNE"] = parseFloat(values[3]);
- record_representation["weight"] = parseInt(values[4]);
-
- return record_representation;
+
+/**
+* cleanJSON
+*
+* @param json, a JSON string that is not correctly formatted.
+*
+* Quick and dirty little function to clean up an Asterix JSON quirk.
+*/
+function cleanJSON(json) {
+ return json
+ .replace("rectangle", '"rectangle"')
+ .replace("point:", '"point":')
+ .replace("point:", '"point":')
+ .replace("int64", '"int64"');
}
+
/**
* A spatial data cleaning and mapping call
* @param {Object} res, a result object from a cherry geospatial query
*/
function cherryQuerySyncCallback(res) {
- records = res["results"];
- if (typeof res["results"][0] == "object") {
- records = res["results"][0];
- }
-
+ // Initialize coordinates and weights, to store
+ // coordinates of map cells and their weights
+ // TODO these are all included in coordinates already...
var coordinates = [];
var weights = [];
+ var al = 1;
+
+ // Parse resulting JSON objects. Here is an example record:
+ // { "cell": { rectangle: [{ point: [22.5, 64.5]}, { point: [24.5, 66.5]}]}, "count": { int64: 5 }}
+ $.each(res.results, function(i, data) {
+
+ // First, parse a JSON object from a cleaned up string.
+ var record = $.parseJSON(cleanJSON(data));
+
+ // Parse Coordinates and Weights into a record
+ var sw = record.cell.rectangle[0].point;
+ var ne = record.cell.rectangle[1].point;
- for (var subrecord in records) {
- var coordinate = getRecord(records[subrecord]);
+ var coordinate = {
+ "latSW" : sw[0],
+ "lngSW" : sw[1],
+ "latNE" : ne[0],
+ "lngNE" : ne[1],
+ "weight" : record.count.int64
+ }
+
weights.push(coordinate["weight"]);
coordinates.push(coordinate);
- }
+ });
triggerUIUpdate(coordinates, weights);
- $("#submit-button").attr("disabled", false);
}
/**
* Triggers a map update based on a set of spatial query result cells
* @param [Array] mapPlotData, an array of coordinate and weight objects
-* @param [Array] params, an object containing original query parameters [LEGACY]
* @param [Array] plotWeights, a list of weights of the spatial cells - e.g., number of tweets
*/
function triggerUIUpdate(mapPlotData, plotWeights) {
@@ -547,7 +566,6 @@
};
var map_circle = new google.maps.Circle(map_circle_options);
map_circle.val = mapPlotData[m];
- map_circle.ind = m;
map_info_windows[m] = new google.maps.InfoWindow({
content: mapPlotData[m].weight + "",
@@ -557,7 +575,9 @@
// Clicking on a circle drills down map to that value, hovering over it displays a count
// of tweets at that location.
google.maps.event.addListener(map_circle, 'click', function (event) {
- map_info_windows[m].close();
+ $.each(map_info_windows, function(i) {
+ map_info_windows[i].close();
+ });
onMapPointDrillDown(map_circle.val);
});
@@ -707,10 +727,14 @@
var save_metacomment_target_tweet = '"' + tweetId + '"';
// Make sure content is entered, and then save this comment.
- if (save_metacomment_target_tweetbook.length == 0) {
- alert("Please enter a tweetbook.");
- } else if ($("#modal-body-add-note").val() == "") {
- alert("Please enter a comment.");
+ if ($("#modal-body-add-note").val() == "") {
+
+ addFailureBlock("Please enter a comment.", "modal-body-message-holder");
+
+ } else if ($("#modal-body-add-to").val() == "") {
+
+ addFailureBlock("Please enter a tweetbook.", "modal-body-message-holder");
+
} else {
// Check if tweetbook exists. If not, create it.
@@ -729,10 +753,11 @@
var successMessage = "Saved comment on <b>Tweet #" + tweetId +
"</b> in dataset <b>" + save_metacomment_target_tweetbook + "</b>.";
- addSuccessBlock(successMessage, "modal-body-message-holder");
+ addSuccessBlock(successMessage, "modal-body-message-holder");
$("#modal-body-add-to").val('');
$("#modal-body-add-note").val('');
+ $("#modal-body-message-holder").html('');
}
});
}
@@ -1106,6 +1131,8 @@
map_tweet_markers[m].setMap(null);
}
map_tweet_markers = [];
+
+ $("#submit-button").attr("disabled", false);
}
/**