From 458c48b229cfd71d35943c16a8ec44df0d2ec65b Mon Sep 17 00:00:00 2001 From: "Brian E. Granger" Date: Fri, 5 Aug 2011 22:06:45 -0700 Subject: [PATCH] File upload/import working from notebook browser. --- IPython/frontend/html/notebook/handlers.py | 6 +- .../frontend/html/notebook/notebookmanager.py | 45 +++- .../html/notebook/static/css/nbbrowser.css | 20 +- .../html/notebook/static/js/nbbrowser_main.js | 7 +- .../html/notebook/static/js/notebooklist.js | 227 ++++++++++++++---- .../html/notebook/templates/nbbrowser.html | 9 +- 6 files changed, 243 insertions(+), 71 deletions(-) diff --git a/IPython/frontend/html/notebook/handlers.py b/IPython/frontend/html/notebook/handlers.py index 630c3bd65..7b618fbde 100644 --- a/IPython/frontend/html/notebook/handlers.py +++ b/IPython/frontend/html/notebook/handlers.py @@ -89,8 +89,9 @@ class NotebookRootHandler(web.RequestHandler): nbm = self.application.notebook_manager body = self.request.body.strip() format = self.get_argument('format', default='json') + name = self.get_argument('name', default=None) if body: - notebook_id = nbm.save_new_notebook(body, format) + notebook_id = nbm.save_new_notebook(body, name=name, format=format) else: notebook_id = nbm.new_notebook() self.set_header('Location', '/'+notebook_id) @@ -120,7 +121,8 @@ class NotebookHandler(web.RequestHandler): def put(self, notebook_id): nbm = self.application.notebook_manager format = self.get_argument('format', default='json') - nbm.save_notebook(notebook_id, self.request.body, format) + name = self.get_argument('name', default=None) + nbm.save_notebook(notebook_id, self.request.body, name=name, format=format) self.set_status(204) self.finish() diff --git a/IPython/frontend/html/notebook/notebookmanager.py b/IPython/frontend/html/notebook/notebookmanager.py index adf1433ce..d66d35970 100644 --- a/IPython/frontend/html/notebook/notebookmanager.py +++ b/IPython/frontend/html/notebook/notebookmanager.py @@ -124,30 +124,57 @@ class NotebookManager(Configurable): raise web.HTTPError(404) return last_modified, nb - def save_new_notebook(self, data, format=u'json'): - """Save a new notebook and return its notebook_id.""" + def save_new_notebook(self, data, name=None, format=u'json'): + """Save a new notebook and return its notebook_id. + + If a name is passed in, it overrides any values in the notebook data + and the value in the data is updated to use that value. + """ if format not in self.allowed_formats: raise web.HTTPError(415) + try: nb = current.reads(data, format) except: - raise web.HTTPError(400) - try: - name = nb.name - except AttributeError: - raise web.HTTPError(400) + if format == u'xml': + # v1 notebooks might come in with a format='xml' but be json. + try: + nb = current.reads(data, u'json') + except: + raise web.HTTPError(400) + else: + raise web.HTTPError(400) + + if name is None: + try: + name = nb.name + except AttributeError: + raise web.HTTPError(400) + nb.name = name + notebook_id = self.new_notebook_id(name) self.save_notebook_object(notebook_id, nb) return notebook_id - def save_notebook(self, notebook_id, data, format=u'json'): + def save_notebook(self, notebook_id, data, name=None, format=u'json'): """Save an existing notebook by notebook_id.""" if format not in self.allowed_formats: raise web.HTTPError(415) + try: nb = current.reads(data, format) except: - raise web.HTTPError(400) + if format == u'xml': + # v1 notebooks might come in with a format='xml' but be json. + try: + nb = current.reads(data, u'json') + except: + raise web.HTTPError(400) + else: + raise web.HTTPError(400) + + if name is not None: + nb.name = name self.save_notebook_object(notebook_id, nb) def save_notebook_object(self, notebook_id, nb): diff --git a/IPython/frontend/html/notebook/static/css/nbbrowser.css b/IPython/frontend/html/notebook/static/css/nbbrowser.css index 45c821f0f..9d730969d 100644 --- a/IPython/frontend/html/notebook/static/css/nbbrowser.css +++ b/IPython/frontend/html/notebook/static/css/nbbrowser.css @@ -18,12 +18,22 @@ body { overflow: auto; } +#left_panel { +} + +#drop_zone { + height: 200px; + width: 200px +} + #content_panel { width: 600px; } #content_toolbar { - padding: 10px 5px; + padding: 10px 5px 5px 5px; + height: 25px; + line-height: 25px; } #header_border { @@ -35,6 +45,10 @@ body { width: 100%; } +#drag_info { + float: left; +} + #notebooks_buttons { float: right; } @@ -59,6 +73,10 @@ body { float: right; } +.item_buttons .upload_button { + color: darkred; +} + .highlight_text { color: blue; } diff --git a/IPython/frontend/html/notebook/static/js/nbbrowser_main.js b/IPython/frontend/html/notebook/static/js/nbbrowser_main.js index c82a720b5..f9ca12e1d 100644 --- a/IPython/frontend/html/notebook/static/js/nbbrowser_main.js +++ b/IPython/frontend/html/notebook/static/js/nbbrowser_main.js @@ -10,14 +10,17 @@ $(document).ready(function () { $('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content'); $('div#main_app').addClass('border-box-sizing ui-widget'); - $('div#app_hbox').addClass('hbox center'); + $('div#app_hbox').addClass('hbox'); $('div#content_toolbar').addClass('ui-widget ui-helper-clearfix'); - $('#new_notebook').click(function (e) { + $('#new_notebook').button().click(function (e) { window.open('/new'); }); + $('div#left_panel').addClass('box-flex'); + $('div#right_panel').addClass('box-flex'); + IPython.notebook_list = new IPython.NotebookList('div#notebook_list'); IPython.notebook_list.load_list(); diff --git a/IPython/frontend/html/notebook/static/js/notebooklist.js b/IPython/frontend/html/notebook/static/js/notebooklist.js index e0af447f3..76dfdfa51 100644 --- a/IPython/frontend/html/notebook/static/js/notebooklist.js +++ b/IPython/frontend/html/notebook/static/js/notebooklist.js @@ -21,7 +21,35 @@ var IPython = (function (IPython) { NotebookList.prototype.bind_events = function () { - + var that = this; + this.element.bind('dragover', function () { + return false; + }); + this.element.bind('drop', function (event) { + var files = event.originalEvent.dataTransfer.files; + for (var i = 0, f; f = files[i]; i++) { + var reader = new FileReader(); + reader.readAsText(f); + var fname = f.name.split('.'); + var nbname = fname[0]; + var nbformat = fname[1]; + if (nbformat === 'ipynb') {nbformat = 'xml';}; + if (nbformat === 'xml' || nbformat === 'py' || nbformat === 'json') { + var item = that.new_notebook_item(0); + that.add_name_input(nbname, item); + item.data('nbformat', nbformat); + // Store the notebook item in the reader so we can use it later + // to know which item it belongs to. + $(reader).data('item', item); + reader.onload = function (event) { + var nbitem = $(event.target).data('item'); + that.add_notebook_data(event.target.result, nbitem); + that.add_upload_button(nbitem); + }; + }; + } + return false; + }); }; @@ -31,7 +59,7 @@ var IPython = (function (IPython) { cache : false, type : "GET", dataType : "json", - success : $.proxy(this.list_loaded,this) + success : $.proxy(this.list_loaded, this) }; $.ajax("/notebooks", settings); }; @@ -39,64 +67,157 @@ var IPython = (function (IPython) { NotebookList.prototype.list_loaded = function (data, status, xhr) { var len = data.length; + // Todo: remove old children for (var i=0; i'); - item.addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix'); - var item_name = $('').addClass('item_name').append( - $('').attr('href','/'+notebook_id). - attr('target','_blank'). - text(nbname) - ); - // Store the nbname and notebook_id on the item for later usage. We have to do this - // because the loop over elements changes the values of the local nbname and notebook_id - // variables. - item.data('notebook_id',notebook_id); - item.data('nbname',nbname); - var buttons = $('').addClass('item_buttons'); - var delete_button = $('').button(). - click(function (e) { - // $(this) is the button that was clicked. - var that = $(this); - // We use the nbname and notebook_id from the parent notebook_item element's - // data because the outer scopes values change as we iterate through the loop. - var parent_item = that.parents('div.notebook_item'); - var nbname = parent_item.data('nbname'); - var notebook_id = parent_item.data('notebook_id'); - var dialog = $('
'); - dialog.html('Are you sure you want to permanently delete the notebook: ' + nbname + '?'); - parent_item.append(dialog); - dialog.dialog({ - resizable: false, - modal: true, - title: "Delete notebook", - buttons : { - "Delete": function () { - var settings = { - processData : false, - cache : false, - type : "DELETE", - dataType : "json", - success : function (data, status, xhr) { - parent_item.remove(); - } - }; - $.ajax("/notebooks/" + notebook_id, settings); - $(this).dialog('close'); - }, - "Cancel": function () { - $(this).dialog('close'); - } - } - }); - }); - buttons.append(delete_button); - item.append(item_name).append(buttons); + NotebookList.prototype.new_notebook_item = function (index) { + var item = $('
'); + item.addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix'); + var item_name = $('').addClass('item_name'); + + item.append(item_name); + if (index === -1) { this.element.append(item); + } else { + this.element.children().eq(index).after(item); } + return item; + }; + + + NotebookList.prototype.add_link = function (notebook_id, nbname, item) { + item.data('nbname', nbname); + item.data('notebook_id', notebook_id); + var new_item_name = $('').addClass('item_name'); + new_item_name.append( + $(''). + attr('href','/'+notebook_id). + attr('target','_blank'). + text(nbname) + ); + var e = item.find('.item_name'); + if (e.length === 0) { + item.append(new_item_name); + } else { + e.replaceWith(new_item_name); + }; + }; + + + NotebookList.prototype.add_name_input = function (nbname, item) { + item.data('nbname', nbname); + var new_item_name = $('').addClass('item_name'); + new_item_name.append( + $('').addClass('ui-widget ui-widget-content'). + attr('value', nbname). + attr('size', '30'). + attr('type', 'text') + ); + var e = item.find('.item_name'); + if (e.length === 0) { + item.append(new_item_name); + } else { + e.replaceWith(new_item_name); + }; + }; + + + NotebookList.prototype.add_notebook_data = function (data, item) { + item.data('nbdata',data); + }; + + + NotebookList.prototype.add_delete_button = function (item) { + var new_buttons = $('').addClass('item_buttons'); + var delete_button = $('').button(). + click(function (e) { + // $(this) is the button that was clicked. + var that = $(this); + // We use the nbname and notebook_id from the parent notebook_item element's + // data because the outer scopes values change as we iterate through the loop. + var parent_item = that.parents('div.notebook_item'); + var nbname = parent_item.data('nbname'); + var notebook_id = parent_item.data('notebook_id'); + var dialog = $('
'); + dialog.html('Are you sure you want to permanently delete the notebook: ' + nbname + '?'); + parent_item.append(dialog); + dialog.dialog({ + resizable: false, + modal: true, + title: "Delete notebook", + buttons : { + "Delete": function () { + var settings = { + processData : false, + cache : false, + type : "DELETE", + dataType : "json", + success : function (data, status, xhr) { + parent_item.remove(); + } + }; + $.ajax("/notebooks/" + notebook_id, settings); + $(this).dialog('close'); + }, + "Cancel": function () { + $(this).dialog('close'); + } + } + }); + }); + new_buttons.append(delete_button); + var e = item.find('.item_buttons'); + if (e.length === 0) { + item.append(new_buttons); + } else { + e.replaceWith(new_buttons); + }; + }; + + + NotebookList.prototype.add_upload_button = function (item) { + var that = this; + var new_buttons = $('').addClass('item_buttons'); + var upload_button = $('').button(). + click(function (e) { + var nbname = item.find('.item_name > input').attr('value'); + var nbformat = item.data('nbformat'); + var nbdata = item.data('nbdata'); + var settings = { + processData : false, + cache : false, + type : "POST", + dataType : "json", + data : nbdata, + success : function (data, status, xhr) { + that.add_link(data, nbname, item); + that.add_delete_button(item); + } + }; + + var qs = $.param({name:nbname, format:nbformat}); + $.ajax("/notebooks?" + qs, settings); + }); + var cancel_button = $('').button(). + click(function (e) { + item.remove(); + }); + upload_button.addClass('upload_button'); + new_buttons.append(upload_button).append(cancel_button); + var e = item.find('.item_buttons'); + if (e.length === 0) { + item.append(new_buttons); + } else { + e.replaceWith(new_buttons); + }; }; diff --git a/IPython/frontend/html/notebook/templates/nbbrowser.html b/IPython/frontend/html/notebook/templates/nbbrowser.html index b3b3aaae5..f95afa156 100644 --- a/IPython/frontend/html/notebook/templates/nbbrowser.html +++ b/IPython/frontend/html/notebook/templates/nbbrowser.html @@ -29,11 +29,12 @@
- - +
+
+ Drag files onto the list to import notebooks. @@ -44,8 +45,8 @@
- - +
+