From 328c05636b6ff60608480ed8f3c2e1f4dae1f371 Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Wed, 13 Mar 2013 18:52:27 +0100 Subject: [PATCH] first stab at querycode - works but one needs to select the lang for now --- engine/plugin.py | 1 + engine/static/js/manifold-pubsub.js | 18 +-- engine/templates/plugin.html | 2 +- plugins/querycode.py | 29 +++++ plugins/static/css/querycode.css | 185 ++++++++++++++++++++++++++++ plugins/static/js/querycode.js | 185 ++++++++++++++++++++++++++++ plugins/static/js/simplelist.js | 44 +++---- plugins/templates/querycode.html | 7 ++ trash/dashboard.py | 28 +++-- 9 files changed, 459 insertions(+), 40 deletions(-) create mode 100644 plugins/querycode.py create mode 100644 plugins/static/css/querycode.css create mode 100644 plugins/static/js/querycode.js create mode 100644 plugins/templates/querycode.html diff --git a/engine/plugin.py b/engine/plugin.py index 2088c270..ad88024c 100644 --- a/engine/plugin.py +++ b/engine/plugin.py @@ -130,6 +130,7 @@ class Plugin: # shove this into plugin.html env = {} env ['plugin_content']= plugin_content + self.need_spin=self.is_asynchroneous() env.update(self.__dict__) result = render_to_string ('plugin.html',env) diff --git a/engine/static/js/manifold-pubsub.js b/engine/static/js/manifold-pubsub.js index dc5c6f72..54dba525 100644 --- a/engine/static/js/manifold-pubsub.js +++ b/engine/static/js/manifold-pubsub.js @@ -104,12 +104,12 @@ function clone_object(obj) { }(jQuery)); -function executeFunctionByName(functionName, context /*, args */) { - var args = Array.prototype.slice.call(arguments).splice(2); - var namespaces = functionName.split("."); - var func = namespaces.pop(); - for(var i = 0; i < namespaces.length; i++) { - context = context[namespaces[i]]; - } - return context[func].apply(this, args); -} +//function executeFunctionByName(functionName, context /*, args */) { +// var args = Array.prototype.slice.call(arguments).splice(2); +// var namespaces = functionName.split("."); +// var func = namespaces.pop(); +// for(var i = 0; i < namespaces.length; i++) { +// context = context[namespaces[i]]; +// } +// return context[func].apply(this, args); +//} diff --git a/engine/templates/plugin.html b/engine/templates/plugin.html index 19e0b8b0..7a09e427 100644 --- a/engine/templates/plugin.html +++ b/engine/templates/plugin.html @@ -1,6 +1,6 @@ {##} {% if visible %} -
+
{% if togglable %} {% if not toggled %}

Show {{ title }} ({{ classname }})

diff --git a/plugins/querycode.py b/plugins/querycode.py new file mode 100644 index 00000000..e8b80bf4 --- /dev/null +++ b/plugins/querycode.py @@ -0,0 +1,29 @@ +from engine.plugin import Plugin + +class QueryCode (Plugin): + + def __init__ (self, query, **settings): + Plugin.__init__ (self, **settings) + self.query=query + + def template_file (self): + return "querycode.html" + + def requirements (self): + return { + 'js_files' : [ "js/querycode.js", "js/plugin.js", "js/query.js", "js/onavail.js", + "js/manifold-pubsub.js", "js/manifold-async.js", "spin/spin.all.js", +# Plugins::add_js('/QueryCode/beautyofcode/scripts/shCore.js'); +# Plugins::add_js('/QueryCode/beautyofcode/scripts/shBrushPython.js'); +# Plugins::add_js('/QueryCode/beautyofcode/scripts/shBrushRuby.js'); +# Plugins::add_js('/QueryCode/beautyofcode/scripts/shAutoloader.js'); + ] , + 'css_files': [ "css/querycode.css" , +# Plugins::add_css('/QueryCode/beautyofcode/styles/shCore.css'); +# Plugins::add_css('/QueryCode/beautyofcode/styles/shCoreDefault.css'); +# Plugins::add_css('/QueryCode/beautyofcode/styles/shThemeDefault.css'); + ], + } + + def json_settings_list (self): return ['plugin_uuid', 'query','query_uuid'] + diff --git a/plugins/static/css/querycode.css b/plugins/static/css/querycode.css new file mode 100644 index 00000000..7d6f86ed --- /dev/null +++ b/plugins/static/css/querycode.css @@ -0,0 +1,185 @@ +body { font-family:Arial, Helvetica, Sans-Serif; font-size:0.8em;} + +#report { +border-collapse:collapse; +width: 100%; +} +#report h4 { margin:0px; padding:0px;} +#report img { float:right;} +#report ul { margin:10px 0 10px 40px; padding:0px;} +#report th { background:#C7DDEE; color:#000; padding:7px 15px; text-align:left;} +#report td { background:#fff; color:#000; padding:7px 15px; } +#report tr.odd td { background:#D7DDE3; cursor:pointer; } +#report div.arrow { background:transparent url(images/arrows.png) no-repeat scroll 0px -16px; width:16px; height:16px; display:block;} +#report div.up { background-position:0px 0px;} + +div.info{ + position:relative; /*this is the key*/ + float: right; + z-index:24; + background: url(images/tooltip.png) no-repeat; + //background-color:#ccc; + width: 16px; + height: 16px; + color:#000; + text-decoration:none} + +div.info:hover{ + z-index:25; + //background-color:#ff0 +} + +div.info span{display: none} + +div.info:hover span{ /*the span will display just on :hover state*/ + display:block; + position:absolute; + //top:1em; + left:-22em; + width: 20em; + font-size: 8pt; + border:1px solid #ccdddd; + background-color:#ddeeee; + color:#000; + text-align: left; + padding: 0em 0em 0em 1em; +} + +div.method { + padding: 10px; + background-color: #fafafa; + border-top: solid 1px black; + border-left: solid 1px black; + border-right: solid 1px black; +} +div.ts { + padding: 10px; + background-color: #f5f5f5; + border-left: solid 1px black; + border-right: solid 1px black; +} +div.input { + padding: 10px; + background-color: #fafafa; + border-left: solid 1px black; + border-right: solid 1px black; +} +div.output { + padding: 10px; + background-color: #f5f5f5; + border-left: solid 1px black; + border-right: solid 1px black; + border-bottom: solid 1px black; +} + +#viz { + padding: 10px; + background-color: #fafafa; + border: solid 1px black; + margin-bottom: 10px; +} + +#colcfg { + padding: 10px; + background-color: #fafafa; + border: solid 1px black; + margin-bottom: 10px; +} + +#results { + border-top: solid 1px black; + margin-top: 20px; + padding-top: 20px; +} + +td.center { + background: #ffffff; +} + +#boxes { + font-family: Arial, sans-serif; + list-style-type: none; + margin: 0px; + padding: 0px; + width: 100%; +} +#boxes li { + list-style-type: none; + cursor: move; + position: relative; + float: left; + margin: 2px 2px 0px 0px; + //width: 33px; + height: 33px; + border: 1px solid #e0e0e0; + text-align: center; + padding: 5px; + background-color: #fafafa; + -moz-border-radius: 5px; + border-radius: 5px; +} + +table tr.even.row_selected td { + background-color: #B0BED9; +} + +table tr.odd.row_selected td { + background-color: #9FAFD1; +} + +#coltab tr { + margin: 0px; +} + +#coltab td { + margin: 0px; + border-bottom: none; //1px solid #E9E9E9; + padding: 0px; +} + +div.out{background-color:white; color:black} +div.in{background-color:#CAE8EA; color:#4f6b72} +div.selected{background-color:gray; color:black} +div.invisible{display:none} + +div.clean{ + border: 1px solid #850000; +} + +.clean td, .clean th{ + border: 2px solid #bbb; + background: #ddd; + padding: 5px 10px; + text-align: center; + border-radius: 2px; +} + +.clean table{ + margin: auto; + margin-top: 15px; + margin-bottom: 15px; +} + +.clean caption{ + font-weight: bold; + +} + +.gvChart,.clean{ + border: 2px solid #850000; + border-radius: 5px; + -moz-border-radius: 10px; + width: 720px; + + margin: auto; + margin-top: 20px; +} + +pre{ + background: #eee; + padding: 10px; + border-radius: 10px; + -moz-border-radius: 10px; + white-space: pre-wrap; +} + diff --git a/plugins/static/js/querycode.js b/plugins/static/js/querycode.js new file mode 100644 index 00000000..a48ff37b --- /dev/null +++ b/plugins/static/js/querycode.js @@ -0,0 +1,185 @@ +/** + * MySlice QueryCode plugin + * URL: http://trac.myslice.info + * Description: display code for a target query in python or ruby + * Author: The MySlice Team + * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA + * License: GPLv3 + */ + +querycode_debug=false; +querycode_debug=true; + +function debug_object (msg, o) { + var keys=[]; + for (var k in o) keys.push(k); + console.log (msg + " Keys : " + keys); +} + +(function($) { + + var methods = { + init : function (options) { + return this.each(function() { + var $this=$(this); + var data=$this.data('QueryCode'); + if ( ! data ) { + // Subscribe to query updates + var channel='/results/' + options.query_uuid + '/updated'; + /* passing $this as 2nd arg: callbacks will retrieve $this as e.data */ + $.subscribe(channel, $this, update_plugin); + if (querycode_debug) window.console.log('subscribing to ' + channel); + $this.data('QueryCode', {options: options}); + // react to changes to the language selector + $this.find(".querycode-lang").change(change_language); + } + }); + + console.log("temporarily turned off SyntaxHighlighter ..."); +// SyntaxHighlighter.all(); + + }, + +// destroy : function( ) { +// if (querycode_debug) console.log("QueryCode.destroy..."); +// }, +// update : function( content ) { +// if (querycode_debug) console.log("QueryCode.update..."); +// }, + trigger : function () { + var channel='/results/' + $(this).data.QueryCode.options.query_uuid + '/updated'; + publish(channel,"trigger"); + } + + + } // methods + + $.fn.QueryCode = 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 jQuery.QueryCode' ); + } + }; + + // we retrieve the plugindiv as e.data - cf the 2nd arg to subscribe + function update_plugin (e) { + var $plugindiv=e.data; + do_update ($plugindiv); + } + + // linked to 'change' on the selector; this=the selector dom + function change_language (e) { + var $plugindiv = $(this).closest(".plugin"); + do_update($plugindiv); + } + + function do_update ($plugindiv) { + // just in case + $plugindiv.closest('.need-spin').spin(false); + + var lang=$plugindiv.find(".querycode-lang").val(); + var dom=$plugindiv.find(".querycode-viz"); + var query = $plugindiv.data().QueryCode.options.query; + funname="translate_query_as_" + lang; + fun=eval(funname); + if ( ! fun) { + console.log("Cannot find translator function for lang " + lang); + return; + } + html_code=fun(query); + dom.html(html_code); + console.log("turned off SyntaxHighlighter.highlight"); +// SyntaxHighlighter.highlight() + + } + + + // private stuff + function translate_query_as_ruby (query) { + debug_object("query_ruby entering -- query=" + query, query); + var output = '# Connection to XMLRPC server\n'; + output += 'require "xmlrpc/client"\n'; + output += 'require "pp"\n'; + output += '\n'; + output += 'XMLRPC::Config.module_eval do\n'; + output += ' remove_const :ENABLE_NIL_PARSER\n'; + output += ' const_set :ENABLE_NIL_PARSER, true\n'; + output += 'end\n'; + output += 'srv = XMLRPC::Client.new2("https://www.top-hat.info/API/")\n'; + //output += 'tophat = xmlrpclib.ServerProxy("' . (TOPHAT_API_PORT == 443 ? 'http' : 'https') . '://' . TOPHAT_API_HOST . ':' . TOPHAT_API_PORT . TOPHAT_API_PATH . '", allow_none=True)\n\n'; + output += '\n'; + output += '# Authentication token\n'; + output += 'auth = {"AuthMethod" => "password", "Username" => "guest", "AuthString" => "guest"}\n'; + output += '\n'; + + ifs = ''; + $.each(query.filters, function(i, value) { + if (ifs != '') ifs += ', '; + ifs += '"'; + if (value[1] != "=") + ifs += value[1]; + ifs += value[0] + '" => "' + value[2] + '"'; + }); + ifs = '{' + ifs + '}'; + + ofs = ''; + $.each(query.fields, function(index, value) { + if (ofs != '') + ofs += ', '; + ofs += '"' + value + '"'; + }); + ofs = '[' + ofs + ']'; + + output += 'pp srv.call("' + query.action +'", auth, "' + query.method + '", "' + query.timestamp + '", ' + ifs + ', ' + ofs + ')'; + + var output = '
' + output + "
"; + return output; + + } + + function translate_query_as_python (query) { + // xxx tmp + var TOPHAT_API_HOST="hostname", TOPHAT_API_PORT=443, TOPHAT_API_PATH="/path"; + var proto = (TOPHAT_API_PORT == 443 ? 'https' : 'http'); + var output = '# Connection to XMLRPC server\n'; + output += 'import xmlrpclib\n'; + output += 'srv = xmlrpclib.ServerProxy("' + proto + '://' + TOPHAT_API_HOST + ':' + TOPHAT_API_PORT + TOPHAT_API_PATH + '", allow_none=True)\n\n'; + output += '# Authentication token\n'; + output += 'auth = {"AuthMethod": "password", "Username": "name.surname@domain.name", "AuthString": "mypassword"}\n\n'; + + ifs = ''; + $.each(query.filters, function(i, value) { + if (ifs != '') + ifs += ', '; + //ifs += '"' + //if (value[1] != "=") + // ifs += value[1]; + ifs += '["' + value[0] + '", "' + value[1] + '", "' + value[2] + '"]'; + }); + ifs = '[' + ifs + ']'; + + ofs = ''; + $.each(query.fields, function(index, value) { + if (ofs != '') + ofs += ', '; + ofs += '"' + value + '"'; + }); + ofs = '[' + ofs + ']'; + + output += 'srv.' + query.action + '(auth, "' + query.method + '", ' + ifs + ', {}, ' + ofs + ')'; + var output = '
' + output + "
"; + return output; + } + + + + // get these plugins to update their contents upon loading + // manually publishing on the right channel +// $(function () { $(".QueryCode").each(function () { this.trigger(); })}) + +})(jQuery); // end closure wrapper + diff --git a/plugins/static/js/simplelist.js b/plugins/static/js/simplelist.js index 78622119..3d166a7c 100644 --- a/plugins/static/js/simplelist.js +++ b/plugins/static/js/simplelist.js @@ -1,24 +1,20 @@ /** * MySlice SimpleList plugin - * Version: 0.1.0 - * URL: http://www.myslice.info + * URL: http://trac.myslice.info * Description: display simple lists like slices or testbeds - * Requires: * Author: The MySlice Team * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA * License: GPLv3 */ -simplelist_debug=true; +simplelist_debug=false; +//simplelist_debug=true; (function($){ var methods = { init : function( options ) { return this.each(function(){ - var $this = $(this); - var data = $this.data('SimpleList'); - /* create an empty DOM object */ - var SimpleList = $('
', { text : $this.attr('title') }); + var $this = $(this), data = $this.data('SimpleList'); // If the plugin hasn't been initialized yet if ( ! data ) { /* Subscribe to query updates */ @@ -26,20 +22,23 @@ simplelist_debug=true; /* passing $this as 2nd arg: callbacks will retrieve $this as e.data */ $.subscribe(channel, $this, update_plugin); if (simplelist_debug) window.console.log('subscribing to ' + channel); - $this.data('SimpleList', {options: options, SimpleList : SimpleList}); + $this.data('SimpleList', {options: options}); } }); }, destroy : function( ) { + if (simplelist_debug) console.log("SimpleList.destroy..."); return this.each(function(){ var $this = $(this), data = $this.data('SimpleList'); + // xxx not too sure what this is about $(window).unbind('SimpleList'); - data.SimpleList.remove(); $this.removeData('SimpleList'); - }) - }, - update : function( content ) { } - }; + }); + }, + update : function( content ) { + if (simplelist_debug) console.log("SimpleList.update..."); + }, + }; // methods $.fn.SimpleList = function( method ) { /* Method calling logic */ @@ -53,21 +52,23 @@ simplelist_debug=true; }; /* Private methods */ + // complexity here is mostly because a datatables-enabled table cannot + // be updated in a "normal" way using .html() function update_plugin(e, rows) { // e.data is what we passed in second argument to subscribe // so here it is the jquery object attached to the plugin
- var $this=e.data; + var $plugindiv=e.data; // locate the element; with datatables in the way, // this might not be a direct son of the div-plugin - var $table=$this.find("table.simplelist").first(); + var $table=$plugindiv.find("table.simplelist").first(); // also we may or may not have a header var $tbody=$table.find("tbody.simplelist").first(); var use_datatables = $table.hasClass("with-datatables"); - if (simplelist_debug) console.log($this.attr('id') + " udt= " + use_datatables); + if (simplelist_debug) console.log($plugindiv.attr('id') + " udt= " + use_datatables); // clear the spinning wheel: look up an ancestor that has the need-spin class // do this before we might return - $this.closest('.need-spin').spin(false); + $plugindiv.closest('.need-spin').spin(false); if (rows.length == 0) { if (use_datatables) datatables_set_message ("No result"); @@ -80,7 +81,7 @@ simplelist_debug=true; else regular_set_message (error); return; } - var options = e.data.data().SimpleList.options; + var options = $plugindiv.data().SimpleList.options; if (use_datatables) datatables_update_table ($table,$tbody,rows,options.key); else regular_update_table ($table,$tbody,rows,options.key); @@ -102,9 +103,8 @@ simplelist_debug=true; } function regular_update_table ($table, $tbody, rows, key) { - console.log('regular_update_table ' + rows.length + " rows"); + if (simplelist_debug) console.log('regular_update_table ' + rows.length + " rows"); var html=$.map(rows, function (row) { return html_row ( cell (key, row[key])); }).join(); - console.log("html="+html); $tbody.html(html); } @@ -115,7 +115,7 @@ simplelist_debug=true; } function datatables_update_table ($table, $tbody, rows, key) { - console.log('datatables_update_table ' + rows.length + " rows"); + if (simplelist_debug) console.log('datatables_update_table ' + rows.length + " rows"); $table.dataTable().fnClearTable(); // the lambda here returns a [[]] because $.map is kind of broken; as per the doc: // The function can return any value to add to the array. A returned array will be flattened into the resulting array. diff --git a/plugins/templates/querycode.html b/plugins/templates/querycode.html new file mode 100644 index 00000000..335d5d58 --- /dev/null +++ b/plugins/templates/querycode.html @@ -0,0 +1,7 @@ +Choose your language: + +
+
diff --git a/trash/dashboard.py b/trash/dashboard.py index c61c6e76..ef8639fe 100644 --- a/trash/dashboard.py +++ b/trash/dashboard.py @@ -10,7 +10,9 @@ from django.contrib.auth.decorators import login_required from engine.page import Page from engine.manifoldquery import ManifoldQuery +from plugins.verticallayout import VerticalLayout from plugins.slicelist import SliceList +from plugins.querycode import QueryCode # from myslice.viewutils import topmenu_items, the_user @@ -31,15 +33,25 @@ def dashboard_view (request): sort='slice_hrn',) page.enqueue_query (slices_query) - main_plugin = SliceList ( # setting visible attributes first + main_plugin = VerticalLayout ( page=page, - title='Asynchroneous SliceList', - header='slices list', - with_datatables=False, - toggled=True, - # this is the query at the core of the slice list - query=slices_query, - ) + title="Putting stuff together", + sons=[ + SliceList ( # setting visible attributes first + page=page, + title='Asynchroneous SliceList', + header='slices list', + with_datatables=False, + toggled=False, + # this is the query at the core of the slice list + query=slices_query, + ), + QueryCode ( + page=page, + title="Vizualize your query", + query=slices_query, + ), + ]) # variables that will get passed to the view-plugin.html template template_env = {} -- 2.43.0