first stab at querycode - works but one needs to select the lang for now
authorThierry Parmentelat <thierry.parmentelat@inria.fr>
Wed, 13 Mar 2013 17:52:27 +0000 (18:52 +0100)
committerThierry Parmentelat <thierry.parmentelat@inria.fr>
Wed, 13 Mar 2013 17:52:27 +0000 (18:52 +0100)
engine/plugin.py
engine/static/js/manifold-pubsub.js
engine/templates/plugin.html
plugins/querycode.py [new file with mode: 0644]
plugins/static/css/querycode.css [new file with mode: 0644]
plugins/static/js/querycode.js [new file with mode: 0644]
plugins/static/js/simplelist.js
plugins/templates/querycode.html [new file with mode: 0644]
trash/dashboard.py

index 2088c27..ad88024 100644 (file)
@@ -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)
 
index dc5c6f7..54dba52 100644 (file)
@@ -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);
+//}
index 19e0b8b..7a09e42 100644 (file)
@@ -1,6 +1,6 @@
 {#<!--begin {{ classname }}{{ domid }}-->#}
 {% if visible %}
-<div class='plugin-toggle need-spin' id='toggle-{{ domid }}'>
+<div class='plugin-toggle{% if need_spin %} need-spin{% endif %}' id='toggle-{{ domid }}'>
 {% if togglable %}
   {% if not toggled %}
 <p id='show-{{ domid }}' class='plugin-show'><i class="icon-hand-right"></i> Show {{ title }} ({{ classname }})</p>
diff --git a/plugins/querycode.py b/plugins/querycode.py
new file mode 100644 (file)
index 0000000..e8b80bf
--- /dev/null
@@ -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 (file)
index 0000000..7d6f86e
--- /dev/null
@@ -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 (file)
index 0000000..a48ff37
--- /dev/null
@@ -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 = '<pre class="brush: ruby; toolbar: false;">' + output + "</pre>";
+       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 = '<pre class="brush: python; toolbar: false;">' + output + "</pre>";
+       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
+
index 7862211..3d166a7 100644 (file)
@@ -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 = $('<div />', { 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 <div>
-       var $this=e.data;
+       var $plugindiv=e.data;
        // locate the <table> 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 (file)
index 0000000..335d5d5
--- /dev/null
@@ -0,0 +1,7 @@
+<strong>Choose your language:</strong>
+<select class='querycode-lang'>
+  <option>python</option>
+  <option>ruby</option>
+</select>
+<br/>
+<div class='querycode-viz'></div>
index c61c6e7..ef8639f 100644 (file)
@@ -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 = {}