insert_query : function (query) {
manifold.all_queries[query.query_uuid]=query;
- },
+ },
find_query : function (query_uuid) {
return manifold.all_queries[query_uuid];
},
debug_all_queries : function (msg) {
for (var query_uuid in manifold.all_queries) {
- console.log("manifold.debug " + msg + " " + query_uuid + " -> " + manifold.all_queries[query_uuid]);
+ $.publish("messages:debug","manifold.debug " + msg + " " + query_uuid + " -> " + manifold.all_queries[query_uuid]);
}
},
// trigger a query asynchroneously
proxy_url : '/manifold/proxy/json/',
- asynchroneous_debug : false,
+ asynchroneous_debug : true,
// Executes all async. queries
// input queries are specified as a list of {'query_uuid': <query_uuid>, 'id': <possibly null>}
asynchroneous_exec : function (query_uuid_domids) {
// start spinners
- if (manifold.asynchroneous_debug) console.log("Turning on spin with " + jQuery(".need-spin").length + " matches for .need-spin");
+ if (manifold.asynchroneous_debug)
+ $.publish("messages.debug","Turning on spin with " + jQuery(".need-spin").length + " matches for .need-spin");
jQuery('.need-spin').spin(spin_presets);
// We use js function closure to be able to pass the query (array) to the
var query=manifold.find_query(tuple.query_uuid);
var query_json=JSON.stringify (query);
if (manifold.asynchroneous_debug) {
- console.log ("sending POST on " + manifold.proxy_url + " with query= " + query.__repr());
+ $.publish("messages:debug","sending POST on " + manifold.proxy_url + " with query= " + query.__repr());
}
// not quite sure what happens if we send a string directly, as POST data is named..
// this gets reconstructed on the proxy side with ManifoldQuery.fill_from_POST
},
asynchroneous_success : function (data, query, id) {
- if (manifold.asynchroneous_debug) console.log ("received manifold result with code " + data.code);
+ if (manifold.asynchroneous_debug)
+ $.publish("messages:debug","received manifold result with code " + data.code);
// xxx should have a nicer declaration of that enum in sync with the python code somehow
if (data.code == 1) {
alert("Your session has expired, please log in again");
return this.each(function() {
var $this = $(this);
/* Events */
- $(this).on('show.Datatables', methods.show);
+ $this.on('show.Datatables', methods.show);
/* An object that will hold private variables and methods */
var hazelnut = new Hazelnut (options);
- $(this).data('Hazelnut', hazelnut);
+ $this.data('Hazelnut', hazelnut);
var query_channel = '/query/' + options.query_uuid + '/changed';
var update_channel = '/update-set/' + options.query_uuid;
--- /dev/null
+input.updater {
+ margin: 10px;
+}
--- /dev/null
+<input id="updater-{{ domid }}" class="updater" type=button value="{{ label }}" />
--- /dev/null
+/**
+ * Description: associate with a Get query, maintains the 'Update' query that records pending changes
+ * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA
+ * License: GPLv3
+ */
+
+( function ( $ ) {
+
+ $.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);
+ /* Subscribe to query updates */
+ var results_channel = '/results/' + options.query_uuid + '/changed';
+ $.subscribe(results_channel, function (e,rows) { updater.update_slice (e,rows); } );
+ updater.arm_button();
+ });
+ },
+ destroy : function( ) {
+ return this.each(function(){
+ var $this = $(this);
+ $(window).unbind('Updater');
+ data.Updater.remove();
+ $this.removeData('Updater');
+ });
+ },
+
+ 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.subject, null, query.filters,
+ {}, // params
+ query.fields,
+ undefined, /* unique */
+ query.query_uuid, /* tmp */
+ undefined, undefined /* maybe some day I'll get that one */);
+
+ this.arm_button = function () {
+ $('#updater-' + this.options.plugin_uuid).click(this, this.submit_update_request);
+ },
+ this.submit_update_request = function (e) {
+ var update_query = e.data.update_query;
+ $.publish("messages:debug","Updater.submit_update_request " + update_query.__repr());
+ // xxx here - we need a valid query_uuid so the results will make it
+ manifold.asynchroneous_exec ( [ {'query_uuid': xxx, 'id': null}]);
+ // looks like a previous attempt to disable the button while the query is flying
+ //$('#updateslice-' + options.plugin_uuid).prop('disabled', true);
+ }
+ };
+
+ /* Private methods */
+
+ function update_resources(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]);
+ }
+
+ function update_leases(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]);
+ }
+
+ function update_slice(e, rows, query) {
+ /* This function is called twice : get and update */
+
+ 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 */
+ 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
+ }
+
+ }
+
+})( jQuery );
+
--- /dev/null
+from unfold.plugin import Plugin
+
+class Updater (Plugin):
+
+ def __init__ (self, query, label="Update", **settings):
+ Plugin.__init__ (self, **settings)
+ # xxx would make sense to check this is a Get query...
+ self.query=query
+ 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", ],
+ '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 django.contrib.auth.decorators import login_required
from unfold.page import Page
+from manifold.manifoldquery import ManifoldQuery
from plugins.stack.stack import Stack
from plugins.tabs.tabs import Tabs
from plugins.quickfilter.quickfilter import QuickFilter
from plugins.raw.raw import Raw
from plugins.messages.messages import Messages
+from plugins.updater.updater import Updater
from myslice.viewutils import topmenu_items, the_user
from myslice.viewutils import hard_wired_slice_names, hard_wired_list, lorem_p, lorem, quickfilter_criterias
# variables that will get passed to this template
template_env = {}
+ slicename='ple.inria.omftest'
+ main_query = ManifoldQuery (action='get',
+ subject='resource',
+ timestamp='latest',
+ fields=['network','type','hrn','hostname'],
+ filters= [ [ 'slice_hrn', '=', slicename, ] ],
+ )
+
main_plugin = \
+ Stack (
+ page=page,
+ title='thestack',
+ togglable=False,
+ sons=[ \
Messages (
page=page,
title="Runtime messages",
domid="msgs-pre",
- )
+ ),
+ Updater (
+ page=page,
+ title="Update me",
+ query=main_query,
+ label="Update me",
+ ),
+ ])
# define 'unfold1_main' to the template engine
template_env [ 'unfold1_main' ] = main_plugin.render(request)
# which will result in 'foo' being accessible to the template engine
#
def __init__ (self, page, title, domid=None,
- visible=True, togglable=True, toggled=None, **settings):
+ visible=True, togglable=None, toggled=None, **settings):
self.page = page
self.title=title
# callers can provide their domid for css'ing
self.classname=self._py_classname()
self.plugin_classname=self._js_classname()
self.visible=visible
- self.togglable=togglable
- if toggled is not None: self.toggled=toggled
- else: self.toggled=self.default_toggled()
+ if togglable is None: self.togglable=self.default_togglable()
+ else: self.togglable=togglable
+ if toggled is None: self.toggled=self.default_toggled()
+ else: self.toggled=toggled
# what comes from subclasses
for (k,v) in settings.iteritems():
setattr(self,k,v)
def template_file (self): return "undefined_template"
def template_env (self, request): return {}
+ def default_togglable (self): return True
def default_toggled (self): return 'persistent'
# # tell the framework about requirements (for the document <header>)