plugins: updater now inherits from Plugin class, wip for query and record handlers
+ small fixes
this.iter_subqueries = function(callback, data)
{
rec = function(query, callback, data, parent_query) {
+ callback(query, data, parent_query);
jQuery.each(query.subqueries, function(object, subquery) {
rec(subquery, callback, data, query);
});
- callback(query, data, parent_query);
};
if (this.analyzed_query !== undefined)
/* Update requests from plugins */
var SET_ADD = 201;
var SET_REMOVED = 202;
+var RUN_UPDATE = 203;
/* Query status */
var STATUS_NONE = 500; // Query has not been started yet
-function QueryExt(query, parent_query, main_query) {
+function QueryExt(query, parent_query_ext, main_query_ext, update_query_ext, disabled) {
/* Constructor */
if (typeof query == "undefined")
throw "Must pass a query in QueryExt constructor";
this.query = query
- this.parent_query_ext = (typeof parent_query == "undefined") ? false : parent_query
- this.main_query_ext = (typeof main_query == "undefined") ? false : main_query
+ this.parent_query_ext = (typeof parent_query_ext == "undefined") ? null : parent_query_ext
+ this.main_query_ext = (typeof main_query_ext == "undefined") ? null : main_query_ext
+ this.update_query_ext = (typeof update_query_ext == "undefined") ? null : update_query_ext
+ this.disabled = (typeof update_query_ext == "undefined") ? false : disabled
this.status = null;
this.results = null;
- this.update_query = null; // null unless we are a main_query (aka parent_query == null); only main_query_fields can be updated...
+ // update_query null unless we are a main_query (aka parent_query == null); only main_query_fields can be updated...
}
function QueryStore() {
this.insert = function(query)
{
+ // We expect only main_queries are inserted
+
+ /* If the query has not been analyzed, then we analyze it */
if (query.analyzed_query == null) {
query.analyze_subqueries();
}
- query_ext = new QueryExt(query, null, null)
+ /* We prepare the update query corresponding to the main query and store both */
+ /* Note: they have the same UUID */
+
+ // XXX query.change_action() should become deprecated
+ update_query = query.clone();
+ update_query.action = 'update';
+ update_query.analyzed_query.action = 'update';
+ update_query.params = {};
+ update_query_ext = new QueryExt(update_query);
+ console.log("Update query created from Get query", update_query);
+
+ /* We store the main query */
+ query_ext = new QueryExt(query, null, null, update_query_ext, false);
manifold.query_store.main_queries[query.query_uuid] = query_ext;
+ /* Note: the update query does not have an entry! */
+
+
+ // The query is disabled; since it is incomplete until we know the content of the set of subqueries
+ // XXX unless we have no subqueries ???
+ // we will complete with params when records are received... this has to be done by the manager
+ // SET_ADD, SET_REMOVE will change the status of the elements of the set
+ // UPDATE will change also, etc.
+ // XXX We need a proper structure to store this information...
// We also need to insert all queries and subqueries from the analyzed_query
// XXX We need the root of all subqueries
sq_ext = new QueryExt(sq, parent_query_ext, query_ext)
manifold.query_store.analyzed_queries[sq.query_uuid] = sq_ext;
});
+
+ // XXX We have spurious update queries...
}
/* Searching */
} catch (err) { messages.debug("Cannot turn spins on/off " + err); }
},
+ /**************************************************************************
+ * Metadata management
+ **************************************************************************/
+
+ metadata: {
+
+ get_table: function(method)
+ {
+ var table = MANIFOLD_METADATA[method];
+ return (typeof table === 'undefined') ? null : table;
+ },
+
+ get_columns: function(method)
+ {
+ var table = this.get_table(method);
+ if (!table) {
+ return null;
+ }
+
+ return (typeof table.column === 'undefined') ? null : table.column;
+ },
+
+ get_key: function(method)
+ {
+ var table = this.get_table(method);
+ if (!table)
+ return null;
+
+ return (typeof table.key === 'undefined') ? null : table.key;
+ },
+
+
+ get_column: function(method, name)
+ {
+ var columns = this.get_columns(method);
+ if (!columns)
+ return null;
+
+ $.each(columns, function(i, c) {
+ if (c.name == name)
+ return c
+ });
+ return null;
+ },
+
+ get_type: function(method, name)
+ {
+ var table = this.get_table(method);
+ if (!table)
+ return null;
+
+ return (typeof table.type === 'undefined') ? null : table.type;
+ }
+
+ },
+
/**************************************************************************
* Query management
**************************************************************************/
/* query is the query we sent to the backend; we need to find the
* corresponding analyezd_query in manifold.all_queries
*/
+
+ // XXX We might need to update the corresponding update_query here
+ query_ext = manifold.query_store.find_query_ext(query.query_uuid);
+ query = query_ext.query;
+
+ // We don't prepare an update query if the result has more than 1 entry
+ if (result.length == 1) {
+ var res = result[0];
+
+ console.log("Locating update query for updating params", update_query);
+ update_query_ext = query_ext.update_query_ext;
+ update_query = update_query_ext.query;
+
+ // Testing whether the result has subqueries (one level deep only)
+ // iif the query has subqueries
+ var count = 0;
+ var obj = query.analyzed_query.subqueries;
+ for (method in obj) {
+ if (obj.hasOwnProperty(method)) {
+ var key = manifold.metadata.get_key(method);
+ if (!key)
+ continue;
+ if (key.length > 1)
+ continue;
+ key = key[0];
+ var sq_keys = [];
+ var records = res[method];
+ if (!records)
+ continue
+ $.each(records, function (i, obj) {
+ sq_keys.push(obj[key]);
+ });
+ update_query.params[method] = sq_keys;
+ count++;
+ }
+ }
+
+ if (count > 0) {
+ update_query_ext.disabled = false;
+ }
+
+ }
+
+
+
+ // We have results from the main query
+ // inspect subqueries and get the key for each
+
+ // XXX note that we might need the name for each relation, but
+ // this might be for SET_ADD, since we need to recursively find
+ // the path from the main query
+
+
tmp_query = manifold.find_query(query.query_uuid);
manifold.publish_result_rec(tmp_query.analyzed_query, result);
//}
// XXX we can only update subqueries of the main query. Check !
// assert query_ext.parent_query == query_ext.main_query
- update_query = query_ext.parent_query_ext.update_query;
-
+ update_query = query_ext.main_query_ext.update_query_ext.query;
+
+ var path = "";
+ var sq = query_ext;
+ while (sq.parent_query_ext) {
+ if (path != "")
+ path = '.' + path;
+ path = sq.query.object + path;
+ sq = sq.parent_query_ext;
+ }
+
+ update_query.params[path].push(value);
+ console.log('Updated query params', update_query);
// NOTE: update might modify the fields in Get
// NOTE : we have to modify all child queries
// NOTE : parts of a query might not be started (eg slice.measurements, how to handle ?)
case SET_REMOVED:
// Query uuid has been updated with the key of a removed element
break;
+
+ case RUN_UPDATE:
+ update_query = query_ext.main_query_ext.update_query_ext.query;
+
+ manifold.asynchroneous_exec ( [ {'query_uuid': update_query.query_uuid, 'publish_uuid' : query_uuid} ], false);
+ break;
+
case FILTER_ADDED:
break;
case FILTER_REMOVED:
except:
print "metadata.work_offline: failed to decode %s"%offline_filename
manifold_api = ManifoldAPI(self.auth)
- fields = ['table', 'column.name', 'column.qualifier', 'column.type', 'column.is_array', 'column.description']
+ fields = ['table', 'column.name', 'column.qualifier', 'column.type', 'column.is_array', 'column.description', 'column.default', 'key', 'capability']
#fields = ['table', 'column.column',
# 'column.description','column.header', 'column.title',
# 'column.unit', 'column.info_type',
addInfoWindow: function(marker, map)
{
+ var self = this;
google.maps.event.addListener(marker, 'click', function () {
- if(object.infowindow){
- object.infowindow.close();
+ if(self.infowindow){
+ self.infowindow.close();
}
- object.infowindow.setContent(marker.content);// = new google.maps.InfoWindow({ content: marker.content });
- object.infowindow.open(map, marker);
+ self.infowindow.setContent(marker.content);// = new google.maps.InfoWindow({ content: marker.content });
+ self.infowindow.open(map, marker);
// onload of the infowindow on the map, bind a click on a button
- google.maps.event.addListener(object.infowindow, 'domready', function() {
+ google.maps.event.addListener(self.infowindow, 'domready', function() {
jQuery('.map-button').unbind('click');
// jQuery(".map-button").click({instance: instance_, infoWindow: object.infowindow}, button_click);
});
/* GUI MANIPULATION */
- initialize_table: function() {
+ initialize_table: function()
+ {
/* Transforms the table into DataTable, and keep a pointer to it */
var self = this;
actual_options = {
alert('Hazelnut::clear_fields() not implemented');
},
- /* RECORD HANDLERS */
+ /*************************** RECORD HANDLER ***************************/
on_new_record: function(record)
{
+from unfold.plugin import Plugin
+
+class Updater (Plugin):
+
+ def __init__ (self, query, label="Update", **settings):
+ Plugin.__init__ (self, **settings)
+ self.query=query
+ if query.action != "get": print "Updater on non-get query: ",query.action
+ self.label=label
+
+ def template_file (self):
+ return "updater.html"
+
+ def requirements (self):
+ return {
+ 'js_files' : [ "js/updater.js" , "js/manifold.js", "js/manifold-query.js",
+ "js/spin.presets.js", "js/spin.min.js", "js/jquery.spin.js",
+ "js/Math.uuid.js",
+ ],
+ 'css_files' : "css/updater.css",
+ }
+
+ # although this has no query, we need a plugin instance to be created in the js output
+ def export_json_settings (self): return True
+ # the js plugin expects a domid
+ def json_settings_list (self): return [ 'plugin_uuid', 'query_uuid', ]
+
+ # and we don't need a spin wheel
+ def start_with_spin (self): return False
+
+ def default_togglable (self): return False
var debug=false;
// debug=true
- $.fn.Updater = function ( method ) {
- /* Method calling logic */
- if ( methods[method] ) {
- return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
- } else if ( typeof method === 'object' || ! method ) {
- return methods.init.apply( this, arguments );
- } else {
- $.error( 'Method ' + method + ' does not exist on $.Updater' );
- }
- };
-
- var methods = {
- init : function( options ) {
- return this.each(function(){
- var $this = $(this);
- var updater = new Updater (options);
- $this.data('Updater',updater);
- // xxx not tested yet
- var results_channel = '/results/' + options.query_uuid + '/changed';
- $.subscribe(results_channel, function (e,rows) { updater.query_completed (e,rows); } );
- // under test..
- var failed_channel = '/results/' + options.query_uuid + '/failed';
- $.subscribe(failed_channel, $this, function (e,code,output) { updater.query_failed (e, code, output); } );
- });
- },
- destroy : function( ) {
- return this.each(function(){
- var $this = $(this);
- $(window).unbind('Updater');
- data.Updater.remove();
- $this.removeData('Updater');
- });
- },
+ var Updater = Plugin.extend({
+
+ init: function(options, element)
+ {
+ this._super(options, element);
+
+ this.listen_query(options.query_uuid);
+ },
+
+
+ /*************************** PLUGIN EVENTS ****************************/
+
+ /***************************** GUI EVENTS *****************************/
+
+ arm_button: function()
+ {
+ this.el('updater').click(this, this.submit_update_request);
+ },
+
+ submit_update_request: function (e)
+ {
+ var self = e.data;
+
+ manifold.raise_event(self.options.query_uuid, RUN_UPDATE);
- show : function( content ) { }
- };
-
- function Updater (options) {
- this.options=options;
- // xxx should try to locate update_query first, in case we have several Updaters
- // on the same query
- // however the mental model behind the global manifold object for now is
- // to unambiguously find a query based on its query_uuid, which in the joomla
- // implementation wouldn't fly
- // we keep this for a later improvement
- var query=manifold.find_query (options.query_uuid);
- // very rough way of filling this for now
- this.update_query =
- new ManifoldQuery ("update", query.object, null, query.filters,
- {}, // params
- query.fields,
- undefined, /* unique */
- Math.uuid(32,16),
- undefined, undefined /* maybe some day I'll get that one */);
- manifold.insert_query (this.update_query);
- // arm button once document is loaded
- (function(updater) {$(document).ready(function(){updater.arm_button()})})(this);
-
- this.arm_button = function () {
- $('#updater-' + this.options.plugin_uuid).click(this, this.submit_update_request);
- },
- this.submit_update_request = function (e) {
- var query_uuid = e.data.options.query_uuid;
- var update_query = e.data.update_query;
- if (debug) messages.debug("Updater.submit_update_request " + update_query.__repr());
- // actually send the Update query, but publish results as if coming from the original query
- manifold.asynchroneous_exec ( [ {'query_uuid': update_query.query_uuid, 'publish_uuid' : query_uuid} ], false);
- // disable button while the query is flying
- $('#updater-' + e.data.options.plugin_uuid).attr('disabled', 'disabled');
},
- this.query_failed = function (e, code, output) {
- var plugindiv=e.data;
- var updater=plugindiv.data('Updater');
- $('#updater-' + updater.options.plugin_uuid).removeAttr('disabled');
- // just as a means to deom how to retrieve the stuff passed on the channel
- if (debug) messages.debug("retrieved error code " + code + " and output " + output);
- }
+ /************************** GUI MANIPULATION **************************/
+
+ disable_update_button: function()
+ {
+ this.el('updater').attr('disabled', 'disabled');
+ },
+
+ /*************************** QUERY HANDLER ****************************/
+
+ /*************************** RECORD HANDLER ***************************/
+
+ on_query_status_
+
+ /************************** PRIVATE METHODS ***************************/
+
+ /******************************** TODO ********************************/
+
+ /*
+ query_failed: function (e, code, output)
+ {
+ var plugindiv=e.data;
+ var updater=plugindiv.data('Updater');
+ $('#updater-' + updater.options.plugin_uuid).removeAttr('disabled');
+ // just as a means to deom how to retrieve the stuff passed on the channel
+ if (debug)
+ messages.debug("retrieved error code " + code + " and output " + output);
+ },
- this.update_resources = function (e, resources, change) {
- data = e.data.instance.data().Slices;
+ update_resources: function (e, resources, change)
+ {
+ data = e.data.instance.data().Slices;
+
+ data.update_query.params['resource'] = resources
+ $.publish('/update/' + data.options.query_uuid, [data.update_query, true]);
+ },
- data.update_query.params['resource'] = resources
+ update_leases: function (e, leases, change)
+ {
+ data = e.data.instance.data().Slices;
+
+ data.update_query.params['lease'] = leases
$.publish('/update/' + data.options.query_uuid, [data.update_query, true]);
- },
+ },
+
+ query_completed: function (e, rows, query)
+ {
- this.update_leases = function (e, leases, change) {
- data = e.data.instance.data().Slices;
-
- data.update_query.params['lease'] = leases
- $.publish('/update/' + data.options.query_uuid, [data.update_query, true]);
- },
-
- this.query_completed = function (e, rows, query) {
-
- /* This function is called twice : get and update */
- messages.info("updater.query_completed - not implemented yet");
- return;
-
- var data = e.data.instance.data().Slices;
-
- /* Update placeholders and trigger subqueries updates */
- if (rows.length == 0) {
- alert("no result");
- return;
- }
- var slice = rows[0];
-
- /* for get */
- if (data.update_query == null) {
- data.update_query = new Query('update','slice', 'now', query.filter, {"resource": null, "lease": null}, query.fields, 0, data.options.query_uuid);
- }
- /* In case of update the list of resources and leases should be updated accordingly */
-
- /* only for get ? */
- $.each(slice, function(key, value) {
- if (typeof value == 'string') {
- $('#myslice__' + key).html(value);
- }
- });
-
- /* TODO avoid repetitions + made this code generic and plugin-independent */
-
- if (query.method == 'update') {
- // XXX NON, les uuid doivent etre les memes que dans la query Get, cet appel devrait etre fait avant.
- query.analyzed_subqueries();
- }
-
- /* NOTE: Dans le cadre d'un update, on n'a pas besoin de refaire tout
- * le query plan et obtenir toutes les infos, par contre on ne peut pas
- * savoir d'avance quels parametres ont été accordés, changés, etc.
- * Dans le cas général, ca pourrait affecter le query plan...
- * Par contre on n'a pas d'information sur toutes les resources, mais
- * uniquement celles dans la liste. Comment gérer ?
- */
-
- /* Inform child plugins about their respective parts of the results */
- /* Only for get */
+ // This function is called twice : get and update
+ messages.info("updater.query_completed - not implemented yet");
+ return;
+
+ var data = e.data.instance.data().Slices;
+
+ // Update placeholders and trigger subqueries updates
+ if (rows.length == 0) {
+ alert("no result");
+ return;
+ }
+ var slice = rows[0];
+
+ // for get
+ if (data.update_query == null) {
+ data.update_query = new Query('update','slice', 'now', query.filter, {"resource": null, "lease": null}, query.fields, 0, data.options.query_uuid);
+ }
+ // In case of update the list of resources and leases should be updated accordingly
+
+ // only for get ?
+ $.each(slice, function(key, value) {
+ if (typeof value == 'string') {
+ $('#myslice__' + key).html(value);
+ }
+ });
+
+ // TODO avoid repetitions + made this code generic and plugin-independent
+
+ if (query.method == 'update') {
+ // XXX NON, les uuid doivent etre les memes que dans la query Get, cet appel devrait etre fait avant.
+ query.analyzed_subqueries();
+ }
+
+ // NOTE: Dans le cadre d'un update, on n'a pas besoin de refaire tout
+ // le query plan et obtenir toutes les infos, par contre on ne peut pas
+ // savoir d'avance quels parametres ont été accordés, changés, etc.
+ // Dans le cas général, ca pourrait affecter le query plan...
+ // Par contre on n'a pas d'information sur toutes les resources, mais
+ // uniquement celles dans la liste. Comment gérer ?
+
+ // Inform child plugins about their respective parts of the results
+ // Only for get
var r_subq = query.analyzed_query.subqueries['resource'];
var l_subq = query.analyzed_query.subqueries['lease'];
$.publish('/results/' + r_subq.uuid + '/changed', [slice['resource'], r_subq]);
$.publish('/results/' + l_subq.uuid + '/changed', [slice['lease'], l_subq]);
-
- /* Subscribe to get notifications from child plugins */
+
+ // Subscribe to get notifications from child plugins
if (!data.child_subscribe) {
- $.subscribe('/update-set/' + r_subq.uuid, {instance: e.data.instance}, update_resources);
- $.subscribe('/update-set/' + l_subq.uuid, {instance: e.data.instance}, update_leases);
- data.child_subscribe = true
+ $.subscribe('/update-set/' + r_subq.uuid, {instance: e.data.instance}, update_resources);
+ $.subscribe('/update-set/' + l_subq.uuid, {instance: e.data.instance}, update_leases);
+ data.child_subscribe = true
}
-
- }
- }
+
+ }
+ */
+ });
})( jQuery );
-<input id="updater-{{ domid }}" class="updater" type=button value="{{ label }}" />
+<input id="{{domid}}__updater" class="updater" type=button value="{{ label }}" />
+++ /dev/null
-from unfold.plugin import Plugin
-
-class Updater (Plugin):
-
- def __init__ (self, query, label="Update", **settings):
- Plugin.__init__ (self, **settings)
- self.query=query
- if query.action != "get": print "Updater on non-get query: ",query.action
- self.label=label
-
- def template_file (self):
- return "updater.html"
-
- def requirements (self):
- return {
- 'js_files' : [ "js/updater.js" , "js/manifold.js", "js/manifold-query.js",
- "js/spin.presets.js", "js/spin.min.js", "js/jquery.spin.js",
- "js/Math.uuid.js",
- ],
- 'css_files' : "css/updater.css",
- }
-
- # although this has no query, we need a plugin instance to be created in the js output
- def export_json_settings (self): return True
- # the js plugin expects a domid
- def json_settings_list (self): return [ 'plugin_uuid', 'query_uuid', ]
-
- # and we don't need a spin wheel
- def start_with_spin (self): return False
-
- def default_togglable (self): return False
from plugins.active_filters import ActiveFilters
from plugins.quickfilter.quickfilter import QuickFilter
from plugins.messages.messages import Messages
-from plugins.updater.updater import Updater
+from plugins.updater import Updater
-tmp_default_slice='ple.inria.myslicedemo'
+tmp_default_slice='ple.upmc.myslicedemo'
debug = True
@login_required