Show More
@@ -71,8 +71,7 b' class NotebookHandler(IPythonHandler):' | |||||
71 | @web.authenticated |
|
71 | @web.authenticated | |
72 | @json_errors |
|
72 | @json_errors | |
73 | def patch(self, path='', name=None): |
|
73 | def patch(self, path='', name=None): | |
74 | """patch is currently used strictly for notebook renaming. |
|
74 | """PATCH renames a notebook without re-uploading content.""" | |
75 | Changes the notebook name to the name given in data.""" |
|
|||
76 | nbm = self.notebook_manager |
|
75 | nbm = self.notebook_manager | |
77 | if name is None: |
|
76 | if name is None: | |
78 | raise web.HTTPError(400, u'Notebook name missing') |
|
77 | raise web.HTTPError(400, u'Notebook name missing') | |
@@ -90,10 +89,31 b' class NotebookHandler(IPythonHandler):' | |||||
90 | @web.authenticated |
|
89 | @web.authenticated | |
91 | @json_errors |
|
90 | @json_errors | |
92 | def post(self, path='', name=None): |
|
91 | def post(self, path='', name=None): | |
93 |
"""Create a new notebook in the |
|
92 | """Create a new notebook in the specified path. | |
|
93 | ||||
|
94 | POST creates new notebooks. | |||
|
95 | ||||
|
96 | POST /api/notebooks/path : new untitled notebook in path | |||
|
97 | POST /api/notebooks/path/notebook.ipynb : new notebook with name in path | |||
|
98 | If content specified upload notebook, otherwise start empty. | |||
|
99 | """ | |||
94 | nbm = self.notebook_manager |
|
100 | nbm = self.notebook_manager | |
95 | model = self.get_json_body() |
|
101 | model = self.get_json_body() | |
96 | model = nbm.create_notebook_model(model, path) |
|
102 | if name is None: | |
|
103 | # creating new notebook, model doesn't make sense | |||
|
104 | if model is not None: | |||
|
105 | raise web.HTTPError(400, "Model not valid when creating untitled notebooks.") | |||
|
106 | model = nbm.create_notebook_model(path=path) | |||
|
107 | else: | |||
|
108 | if model is None: | |||
|
109 | self.log.info("Creating new Notebook at %s/%s", path, name) | |||
|
110 | model = {} | |||
|
111 | else: | |||
|
112 | self.log.info("Uploading Notebook to %s/%s", path, name) | |||
|
113 | # set the model name from the URL | |||
|
114 | model['name'] = name | |||
|
115 | model = nbm.create_notebook_model(model, path) | |||
|
116 | ||||
97 | location = self.notebook_location(model[u'name'], model[u'path']) |
|
117 | location = self.notebook_location(model[u'name'], model[u'path']) | |
98 | self.set_header(u'Location', location) |
|
118 | self.set_header(u'Location', location) | |
99 | self.set_header(u'Last-Modified', model[u'last_modified']) |
|
119 | self.set_header(u'Last-Modified', model[u'last_modified']) | |
@@ -120,6 +140,29 b' class NotebookHandler(IPythonHandler):' | |||||
120 | self.set_status(204) |
|
140 | self.set_status(204) | |
121 | self.finish() |
|
141 | self.finish() | |
122 |
|
142 | |||
|
143 | class NotebookCopyHandler(IPythonHandler): | |||
|
144 | ||||
|
145 | SUPPORTED_METHODS = ('POST') | |||
|
146 | ||||
|
147 | @web.authenticated | |||
|
148 | @json_errors | |||
|
149 | def post(self, path='', name=None): | |||
|
150 | """Copy an existing notebook.""" | |||
|
151 | nbm = self.notebook_manager | |||
|
152 | model = self.get_json_body() | |||
|
153 | if name is None: | |||
|
154 | raise web.HTTPError(400, "Notebook name required") | |||
|
155 | self.log.info("Copying Notebook %s/%s", path, name) | |||
|
156 | model = nbm.copy_notebook(name, path) | |||
|
157 | location = url_path_join( | |||
|
158 | self.base_project_url, 'api', 'notebooks', | |||
|
159 | model['path'], model['name'], | |||
|
160 | ) | |||
|
161 | self.set_header(u'Location', location) | |||
|
162 | self.set_header(u'Last-Modified', model[u'last_modified']) | |||
|
163 | self.set_status(201) | |||
|
164 | self.finish(json.dumps(model, default=date_default)) | |||
|
165 | ||||
123 |
|
166 | |||
124 | class NotebookCheckpointsHandler(IPythonHandler): |
|
167 | class NotebookCheckpointsHandler(IPythonHandler): | |
125 |
|
168 | |||
@@ -180,6 +223,7 b' _notebook_name_regex = r"(?P<name>[^/]+\\.ipynb)"' | |||||
180 | _notebook_path_regex = "%s/%s" % (_path_regex, _notebook_name_regex) |
|
223 | _notebook_path_regex = "%s/%s" % (_path_regex, _notebook_name_regex) | |
181 |
|
224 | |||
182 | default_handlers = [ |
|
225 | default_handlers = [ | |
|
226 | (r"/api/notebooks/?%s/copy" % _notebook_path_regex, NotebookCopyHandler), | |||
183 | (r"/api/notebooks/?%s/checkpoints" % _notebook_path_regex, NotebookCheckpointsHandler), |
|
227 | (r"/api/notebooks/?%s/checkpoints" % _notebook_path_regex, NotebookCheckpointsHandler), | |
184 | (r"/api/notebooks/?%s/checkpoints/%s" % (_notebook_path_regex, _checkpoint_id_regex), |
|
228 | (r"/api/notebooks/?%s/checkpoints/%s" % (_notebook_path_regex, _checkpoint_id_regex), | |
185 | ModifyNotebookCheckpointsHandler), |
|
229 | ModifyNotebookCheckpointsHandler), |
@@ -87,7 +87,7 b' class NotebookManager(LoggingConfigurable):' | |||||
87 | """ |
|
87 | """ | |
88 | return basename |
|
88 | return basename | |
89 |
|
89 | |||
90 | def list_notebooks(self): |
|
90 | def list_notebooks(self, path=''): | |
91 | """Return a list of notebook dicts without content. |
|
91 | """Return a list of notebook dicts without content. | |
92 |
|
92 | |||
93 | This returns a list of dicts, each of the form:: |
|
93 | This returns a list of dicts, each of the form:: | |
@@ -112,53 +112,51 b' class NotebookManager(LoggingConfigurable):' | |||||
112 | """Update the notebook model and return the model with no content.""" |
|
112 | """Update the notebook model and return the model with no content.""" | |
113 | raise NotImplementedError('must be implemented in a subclass') |
|
113 | raise NotImplementedError('must be implemented in a subclass') | |
114 |
|
114 | |||
115 | def delete_notebook_model(self, name, path): |
|
115 | def delete_notebook_model(self, name, path=''): | |
116 | """Delete notebook by name and path.""" |
|
116 | """Delete notebook by name and path.""" | |
117 | raise NotImplementedError('must be implemented in a subclass') |
|
117 | raise NotImplementedError('must be implemented in a subclass') | |
118 |
|
118 | |||
119 | def create_notebook_model(self, model=None, path=''): |
|
119 | def create_notebook_model(self, model=None, path=''): | |
120 | """Create a new untitled notebook and return its model with no content.""" |
|
120 | """Create a new untitled notebook and return its model with no content.""" | |
121 | untitled = self.increment_filename('Untitled', path) |
|
|||
122 | if model is None: |
|
121 | if model is None: | |
123 | model = {} |
|
122 | model = {} | |
|
123 | if 'content' not in model: | |||
124 | metadata = current.new_metadata(name=u'') |
|
124 | metadata = current.new_metadata(name=u'') | |
125 |
|
|
125 | model['content'] = current.new_notebook(metadata=metadata) | |
126 | model['content'] = nb |
|
126 | if 'name' not in model: | |
127 |
model['name'] = name |
|
127 | model['name'] = self.increment_filename('Untitled', path) | |
128 | model['path'] = path |
|
128 | ||
129 | else: |
|
129 | model['path'] = path | |
130 | name = model.setdefault('name', untitled) |
|
130 | model = self.save_notebook_model(model, model['name'], model['path']) | |
131 | model['path'] = path |
|
|||
132 | model = self.save_notebook_model(model, name, path) |
|
|||
133 | return model |
|
131 | return model | |
134 |
|
132 | |||
135 |
def copy_notebook(self, name, path=' |
|
133 | def copy_notebook(self, name, path=''): | |
136 | """Copy an existing notebook and return its new model.""" |
|
134 | """Copy an existing notebook and return its new model.""" | |
137 | model = self.get_notebook_model(name, path) |
|
135 | model = self.get_notebook_model(name, path) | |
138 | name = os.path.splitext(name)[0] + '-Copy' |
|
136 | name = os.path.splitext(name)[0] + '-Copy' | |
139 | name = self.increment_filename(name, path) + self.filename_ext |
|
137 | name = self.increment_filename(name, path) + self.filename_ext | |
140 | model['name'] = name |
|
138 | model['name'] = name | |
141 |
model = self.save_notebook_model(model, name, path |
|
139 | model = self.save_notebook_model(model, name, path) | |
142 | return model |
|
140 | return model | |
143 |
|
141 | |||
144 | # Checkpoint-related |
|
142 | # Checkpoint-related | |
145 |
|
143 | |||
146 |
def create_checkpoint(self, name, path=' |
|
144 | def create_checkpoint(self, name, path=''): | |
147 | """Create a checkpoint of the current state of a notebook |
|
145 | """Create a checkpoint of the current state of a notebook | |
148 |
|
146 | |||
149 | Returns a checkpoint_id for the new checkpoint. |
|
147 | Returns a checkpoint_id for the new checkpoint. | |
150 | """ |
|
148 | """ | |
151 | raise NotImplementedError("must be implemented in a subclass") |
|
149 | raise NotImplementedError("must be implemented in a subclass") | |
152 |
|
150 | |||
153 |
def list_checkpoints(self, name, path=' |
|
151 | def list_checkpoints(self, name, path=''): | |
154 | """Return a list of checkpoints for a given notebook""" |
|
152 | """Return a list of checkpoints for a given notebook""" | |
155 | return [] |
|
153 | return [] | |
156 |
|
154 | |||
157 |
def restore_checkpoint(self, checkpoint_id, name, path=' |
|
155 | def restore_checkpoint(self, checkpoint_id, name, path=''): | |
158 | """Restore a notebook from one of its checkpoints""" |
|
156 | """Restore a notebook from one of its checkpoints""" | |
159 | raise NotImplementedError("must be implemented in a subclass") |
|
157 | raise NotImplementedError("must be implemented in a subclass") | |
160 |
|
158 | |||
161 |
def delete_checkpoint(self, checkpoint_id, name, path=' |
|
159 | def delete_checkpoint(self, checkpoint_id, name, path=''): | |
162 | """delete a checkpoint for a notebook""" |
|
160 | """delete a checkpoint for a notebook""" | |
163 | raise NotImplementedError("must be implemented in a subclass") |
|
161 | raise NotImplementedError("must be implemented in a subclass") | |
164 |
|
162 |
@@ -1768,15 +1768,12 b' var IPython = (function (IPython) {' | |||||
1768 |
|
1768 | |||
1769 | Notebook.prototype.copy_notebook = function(){ |
|
1769 | Notebook.prototype.copy_notebook = function(){ | |
1770 | var path = this.notebookPath(); |
|
1770 | var path = this.notebookPath(); | |
1771 | var name = {'name': this.notebook_name} |
|
|||
1772 | var settings = { |
|
1771 | var settings = { | |
1773 | processData : false, |
|
1772 | processData : false, | |
1774 | cache : false, |
|
1773 | cache : false, | |
1775 | type : "POST", |
|
1774 | type : "POST", | |
1776 | data: JSON.stringify(name), |
|
|||
1777 | dataType : "json", |
|
|||
1778 | success:$.proxy(function (data, status, xhr){ |
|
1775 | success:$.proxy(function (data, status, xhr){ | |
1779 | notebook_name = data.name; |
|
1776 | var notebook_name = data.name; | |
1780 | window.open(utils.url_path_join( |
|
1777 | window.open(utils.url_path_join( | |
1781 | this._baseProjectUrl, |
|
1778 | this._baseProjectUrl, | |
1782 | 'notebooks', |
|
1779 | 'notebooks', | |
@@ -1787,8 +1784,10 b' var IPython = (function (IPython) {' | |||||
1787 | }; |
|
1784 | }; | |
1788 | var url = utils.url_path_join( |
|
1785 | var url = utils.url_path_join( | |
1789 | this._baseProjectUrl, |
|
1786 | this._baseProjectUrl, | |
1790 | 'notebooks', |
|
1787 | 'api/notebooks', | |
1791 | path |
|
1788 | path, | |
|
1789 | this.notebook_name, | |||
|
1790 | 'copy' | |||
1792 | ); |
|
1791 | ); | |
1793 | $.ajax(url,settings); |
|
1792 | $.ajax(url,settings); | |
1794 | }; |
|
1793 | }; |
@@ -312,7 +312,7 b' var IPython = (function (IPython) {' | |||||
312 | var upload_button = $('<button/>').text("Upload") |
|
312 | var upload_button = $('<button/>').text("Upload") | |
313 | .addClass('btn btn-primary btn-mini upload_button') |
|
313 | .addClass('btn btn-primary btn-mini upload_button') | |
314 | .click(function (e) { |
|
314 | .click(function (e) { | |
315 |
var nbname = item.find('.item_name > input'). |
|
315 | var nbname = item.find('.item_name > input').val(); | |
316 | var nbformat = item.data('nbformat'); |
|
316 | var nbformat = item.data('nbformat'); | |
317 | var nbdata = item.data('nbdata'); |
|
317 | var nbdata = item.data('nbdata'); | |
318 | var content_type = 'application/json'; |
|
318 | var content_type = 'application/json'; | |
@@ -323,8 +323,6 b' var IPython = (function (IPython) {' | |||||
323 | } |
|
323 | } | |
324 | var model = { |
|
324 | var model = { | |
325 | content : JSON.parse(nbdata), |
|
325 | content : JSON.parse(nbdata), | |
326 | name : nbname, |
|
|||
327 | path : that.notebookPath() |
|
|||
328 | }; |
|
326 | }; | |
329 | var settings = { |
|
327 | var settings = { | |
330 | processData : false, |
|
328 | processData : false, | |
@@ -345,7 +343,8 b' var IPython = (function (IPython) {' | |||||
345 | var url = utils.url_path_join( |
|
343 | var url = utils.url_path_join( | |
346 | that.baseProjectUrl(), |
|
344 | that.baseProjectUrl(), | |
347 | 'api/notebooks', |
|
345 | 'api/notebooks', | |
348 | that.notebookPath() |
|
346 | that.notebookPath(), | |
|
347 | nbname + '.ipynb' | |||
349 | ); |
|
348 | ); | |
350 | $.ajax(url, settings); |
|
349 | $.ajax(url, settings); | |
351 | return false; |
|
350 | return false; |
General Comments 0
You need to be logged in to leave comments.
Login now