Browse Source

[notebook] Initial support for export to Jupyter and icon regrouping

Enrico Berti 10 years ago
parent
commit
6ad46ba

+ 2 - 0
desktop/core/src/desktop/static/desktop/ext/js/download.min.js

@@ -0,0 +1,2 @@
+//download.js v3.1, by dandavis; 2008-2015. [CCBY2] see http://danml.com/download.html for tests/usage
+(function(f,a){"function"==typeof define&&define.amd?define([],a):"object"==typeof exports?module.exports=a():f.download=a()})(this,function(){return function(f,a,b){function n(l){var a=l.split(/[:;,]/);l=a[1];var a=("base64"==a[2]?atob:decodeURIComponent)(a.pop()),c=a.length,b=0,d=new Uint8Array(c);for(b;b<c;++b)d[b]=a.charCodeAt(b);return new g([d],{type:l})}function k(a,b){if("download"in d)return d.href=a,d.setAttribute("download",m),d.innerHTML="downloading...",h.body.appendChild(d),setTimeout(function(){d.click(),h.body.removeChild(d),!0===b&&setTimeout(function(){e.URL.revokeObjectURL(d.href)},250)},66),!0;if("undefined"!=typeof safari)return a="data:"+a.replace(/^data:([\w\/\-\+]+)/,"application/octet-stream"),!window.open(a)&&confirm("Displaying New Document\n\nUse Save As... to download, then click back to return to this page.")&&(location.href=a),!0;var c=h.createElement("iframe");h.body.appendChild(c),b||(a="data:"+a.replace(/^data:([\w\/\-\+]+)/,"application/octet-stream")),c.src=a,setTimeout(function(){h.body.removeChild(c)},333)}var e=window;b=b||"application/octet-stream";var c=f,h=document,d=h.createElement("a");f=function(a){return String(a)};var g=e.Blob||e.MozBlob||e.WebKitBlob||f,g=g.call?g.bind(e):Blob,m=a||"download";"true"===String(this)&&(c=[c,b],b=c[0],c=c[1]);if(String(c).match(/^data\:[\w+\-]+\/[\w+\-]+[,;]/))return navigator.msSaveBlob?navigator.msSaveBlob(n(c),m):k(c);a=c instanceof g?c:new g([c],{type:b});if(navigator.msSaveBlob)return navigator.msSaveBlob(a,m);if(e.URL)k(e.URL.createObjectURL(a),!0);else{if("string"==typeof a||a.constructor===f)try{return k("data:"+b+";base64,"+e.btoa(a))}catch(p){return k("data:"+b+","+encodeURIComponent(a))}b=new FileReader,b.onload=function(a){k(this.result)},b.readAsDataURL(a)}return!0}});

+ 88 - 37
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -101,6 +101,7 @@ from desktop.views import _ko
 
 <script src="${ static('desktop/js/hue.utils.js') }"></script>
 
+<script src="${ static('desktop/ext/js/download.min.js') }"></script>
 
 ${ require.config() }
 
@@ -129,65 +130,66 @@ ${ require.config() }
 
 <div class="search-bar" data-bind="visible: ! $root.isPlayerMode()">
   <div class="pull-right" style="padding-right:50px">
-    <a class="btn pointer" title="${ _('Player mode') }" rel="tooltip" data-placement="bottom" data-bind="visible: $root.selectedNotebook() && $root.selectedNotebook().snippets().length > 0, click: function(){ hueUtils.goFullScreen(); $root.isEditing(false); $root.isPlayerMode(true); }" style="display:none">
-      <i class="fa fa-expand"></i>
+    <a class="btn pointer" title="${ _('Edit') }" rel="tooltip" data-placement="bottom" data-bind="click: toggleEditing, css: {'btn-inverse': isEditing}">
+      <i class="fa fa-pencil"></i>
     </a>
-    &nbsp;&nbsp;
+
+    <a class="btn pointer" title="${ _('Sessions') }" rel="tooltip" data-placement="bottom" data-toggle="modal" data-target="#sessionsDemiModal">
+      <i class="fa fa-cogs"></i>
+    </a>
+
     <div class="btn-group">
       <a class="btn dropdown-toggle" data-toggle="dropdown">
-        <i class="fa fa-check-square-o"></i>
-        <i class="fa fa-caret-down"></i>
+        <i class="fa fa-bars"></i>
       </a>
-      <ul class="dropdown-menu">
+      <ul class="dropdown-menu pull-right">
+        <li>
+          <a href="javascript:void(0)" data-bind="visible: $root.selectedNotebook() && $root.selectedNotebook().snippets().length > 0, click: function(){ hueUtils.goFullScreen(); $root.isEditing(false); $root.isPlayerMode(true); }" style="display:none">
+            <i class="fa fa-fw fa-expand"></i> ${ _('Player mode') }
+          </a>
+        </li>
         <li>
           <a href="javascript:void(0)" data-bind="click: function() { $root.selectedNotebook().clearResults() }">
-            <i class="fa fa-play fa-fw"></i> ${ _('Execute all snippets') }
+            <i class="fa fa-fw fa-play"></i> ${ _('Execute all snippets') }
           </a>
         </li>
         <li>
           <a href="javascript:void(0)" data-bind="click: function() { $root.selectedNotebook().clearResults() }">
-            <i class="fa fa-eraser fa-fw"></i> ${ _('Clear all results') }
+            <i class="fa fa-fw fa-eraser"></i> ${ _('Clear all results') }
           </a>
         </li>
         <li>
           <a href="javascript:void(0)" data-bind="click: displayCombinedContent">
-            <i class="fa fa-file-text-o fa-fw"></i> ${ _('Display all Notebook content') }
+            <i class="fa fa-fw fa-file-text-o"></i> ${ _('Display all Notebook content') }
+          </a>
+        </li>
+        <li>
+          <a href="javascript:void(0)" data-toggle="modal" data-target="#importGithubModal">
+            <i class="fa fa-fw fa-github"></i> ${ _('Import from Github') }
+          </a>
+        </li>
+        <li>
+          <a href="javascript:void(0)" data-bind="click: exportJupyterNotebook">
+            <i class="fa fa-fw fa-file-code-o"></i> ${ _('Export to Jupyter') }
           </a>
         </li>
       </ul>
-   </div>
-
-   &nbsp;&nbsp;&nbsp;
-
-   <a class="btn pointer" title="${ _('Edit') }" rel="tooltip" data-placement="bottom" data-bind="click: toggleEditing, css: {'btn-inverse': isEditing}">
-     <i class="fa fa-pencil"></i>
-   </a>
-
-   <a class="btn pointer" title="${ _('Sessions') }" rel="tooltip" data-placement="bottom" data-toggle="modal" data-target="#sessionsDemiModal">
-     <i class="fa fa-cloud"></i>
-   </a>
-
-   &nbsp;&nbsp;&nbsp;
-
-   <a class="btn pointer" title="${ _('Import from Github') }" rel="tooltip" data-placement="bottom" data-toggle="modal" data-target="#importGithubModal">
-     <i class="fa fa-github"></i>
-   </a>
+    </div>
 
-   &nbsp;&nbsp;&nbsp;
 
-   <a class="btn" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }" data-bind="click: saveNotebook">
-     <i class="fa fa-save"></i>
-   </a>
+    &nbsp;&nbsp;&nbsp;
 
-   &nbsp;&nbsp;&nbsp;
+    <a class="btn" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }" data-bind="click: saveNotebook">
+      <i class="fa fa-save"></i>
+    </a>
 
-   <a class="btn" href="${ url('notebook:new') }" title="${ _('New Notebook') }" rel="tooltip" data-placement="bottom">
-     <i class="fa fa-file-o"></i>
-   </a>
+    <a class="btn" href="${ url('notebook:new') }" title="${ _('New Notebook') }" rel="tooltip" data-placement="bottom">
+      <i class="fa fa-file-o"></i>
+    </a>
 
-   <a class="btn" href="${ url('notebook:notebooks') }" title="${ _('Notebooks') }" rel="tooltip" data-placement="bottom">
-     <i class="fa fa-tags"></i>
-   </a>
+    <a class="btn" href="${ url('notebook:notebooks') }" title="${ _('Notebooks') }" rel="tooltip" data-placement="bottom">
+      <i class="fa fa-tags"></i>
+    </a>
   </div>
 
 
@@ -1526,6 +1528,55 @@ ${ require.config() }
 
     var viewModel;
 
+    var exportJupyterNotebook = function () {
+      function addCell(type, code) {
+        var cell = {
+          cell_type: type,
+          source: [
+            code
+          ],
+          metadata: {
+            collapsed: false
+          }
+        }
+        if (type == "code") {
+          cell.outputs = [];
+          cell.execution_count = 0;
+        }
+        return cell;
+      }
+
+      function addCodeCell(code) {
+        return addCell("code", code);
+      }
+
+      function addMarkdownCell(code) {
+        return addCell("markdown", code);
+      }
+
+      var currentNotebook = viewModel.notebooks()[0];
+      var jupyterNotebook = {
+        nbformat: 4,
+        nbformat_minor: 0,
+        cells: [],
+        metadata: {}
+      }
+
+      currentNotebook.snippets().forEach(function (snippet) {
+        if (snippet.type() == "pyspark") {
+          jupyterNotebook.cells.push(addCodeCell(snippet.statement_raw()));
+        }
+        if (snippet.type() == "markdown") {
+          jupyterNotebook.cells.push(addMarkdownCell(snippet.statement_raw()));
+        }
+      });
+
+
+      download(JSON.stringify(jupyterNotebook), currentNotebook.name() + ".ipynb", "text/plain");
+    }
+
+    window.exportJupyterNotebook = exportJupyterNotebook;
+
     var importExternalNotebook = function (notebook) {
       var currentNotebook = viewModel.notebooks()[0];
       currentNotebook.name(notebook.name);