From: Jordan Augé Date: Tue, 13 Aug 2013 13:51:11 +0000 (+0200) Subject: manifold: now managing update queries X-Git-Tag: myslice-0.2-1~57^2~4 X-Git-Url: http://git.onelab.eu/?p=myslice.git;a=commitdiff_plain;h=c82cd94c927d1f2ccf60a0f3b1a014c1425b1727 manifold: now managing update queries plugins: updater now inherits from Plugin class, wip for query and record handlers + small fixes --- diff --git a/manifold/js/manifold-query.js b/manifold/js/manifold-query.js index 1dcd56cd..406026e5 100644 --- a/manifold/js/manifold-query.js +++ b/manifold/js/manifold-query.js @@ -103,10 +103,10 @@ INSERT INTO object VALUES(field=value) 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) diff --git a/manifold/js/manifold.js b/manifold/js/manifold.js index f7c098a4..abbd6e6d 100644 --- a/manifold/js/manifold.js +++ b/manifold/js/manifold.js @@ -35,6 +35,7 @@ var DONE = 102; /* 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 @@ -51,18 +52,20 @@ var STATUS_UPDATE_ERROR = 507; -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() { @@ -74,12 +77,36 @@ 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 @@ -93,6 +120,8 @@ function QueryStore() { 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 */ @@ -143,6 +172,62 @@ var manifold = { } 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 **************************************************************************/ @@ -357,6 +442,59 @@ var manifold = { /* 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); //} @@ -407,8 +545,19 @@ var manifold = { // 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 ?) @@ -422,6 +571,13 @@ var manifold = { 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: diff --git a/manifold/metadata.py b/manifold/metadata.py index f250228b..6f4c5ce6 100644 --- a/manifold/metadata.py +++ b/manifold/metadata.py @@ -26,7 +26,7 @@ class MetaData: 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', diff --git a/plugins/googlemaps/static/js/googlemaps.js b/plugins/googlemaps/static/js/googlemaps.js index e97d1f5f..d72e2773 100644 --- a/plugins/googlemaps/static/js/googlemaps.js +++ b/plugins/googlemaps/static/js/googlemaps.js @@ -121,14 +121,15 @@ 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); }); diff --git a/plugins/hazelnut/static/js/hazelnut.js b/plugins/hazelnut/static/js/hazelnut.js index d26f0ea9..48a392d7 100644 --- a/plugins/hazelnut/static/js/hazelnut.js +++ b/plugins/hazelnut/static/js/hazelnut.js @@ -69,7 +69,8 @@ /* GUI MANIPULATION */ - initialize_table: function() { + initialize_table: function() + { /* Transforms the table into DataTable, and keep a pointer to it */ var self = this; actual_options = { @@ -360,7 +361,7 @@ alert('Hazelnut::clear_fields() not implemented'); }, - /* RECORD HANDLERS */ + /*************************** RECORD HANDLER ***************************/ on_new_record: function(record) { diff --git a/plugins/updater/__init__.py b/plugins/updater/__init__.py index e69de29b..2f68b410 100644 --- a/plugins/updater/__init__.py +++ b/plugins/updater/__init__.py @@ -0,0 +1,31 @@ +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 diff --git a/plugins/updater/static/js/updater.js b/plugins/updater/static/js/updater.js index 7ac0fc7b..e85a5131 100644 --- a/plugins/updater/static/js/updater.js +++ b/plugins/updater/static/js/updater.js @@ -11,157 +11,136 @@ 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 ); diff --git a/plugins/updater/static/updater.html b/plugins/updater/static/updater.html index 7edc44da..798b17af 100644 --- a/plugins/updater/static/updater.html +++ b/plugins/updater/static/updater.html @@ -1 +1 @@ - + diff --git a/plugins/updater/updater.py b/plugins/updater/updater.py deleted file mode 100644 index 2f68b410..00000000 --- a/plugins/updater/updater.py +++ /dev/null @@ -1,31 +0,0 @@ -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 diff --git a/trash/sliceview.py b/trash/sliceview.py index f1591c81..632aec40 100644 --- a/trash/sliceview.py +++ b/trash/sliceview.py @@ -24,9 +24,9 @@ from plugins.query_editor import QueryEditor 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