handlers.py
235 lines
| 8.5 KiB
| text/x-python
|
PythonLexer
|
r10642 | """Tornado handlers for the notebooks web service. | ||
|
r10641 | |||
Authors: | ||||
* Brian Granger | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2008-2011 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
|
r13045 | import json | ||
|
r10641 | |||
|
r13045 | from tornado import web | ||
|
r10641 | |||
|
r13067 | from IPython.html.utils import url_path_join | ||
|
r10641 | from IPython.utils.jsonutil import date_default | ||
|
r13067 | from IPython.html.base.handlers import IPythonHandler, json_errors | ||
|
r10641 | |||
#----------------------------------------------------------------------------- | ||||
# Notebook web service handlers | ||||
#----------------------------------------------------------------------------- | ||||
|
r12984 | |||
|
r10641 | class NotebookHandler(IPythonHandler): | ||
|
r13045 | SUPPORTED_METHODS = (u'GET', u'PUT', u'PATCH', u'POST', u'DELETE') | ||
|
r13067 | def notebook_location(self, name, path=''): | ||
|
r13045 | """Return the full URL location of a notebook based. | ||
Parameters | ||||
---------- | ||||
name : unicode | ||||
|
r13067 | The base name of the notebook, such as "foo.ipynb". | ||
|
r13045 | path : unicode | ||
The URL path of the notebook. | ||||
""" | ||||
|
r13067 | return url_path_join(self.base_project_url, 'api', 'notebooks', path, name) | ||
|
r10641 | |||
|
r11644 | @web.authenticated | ||
|
r13045 | @json_errors | ||
|
r13067 | def get(self, path='', name=None): | ||
""" | ||||
GET with path and no notebook lists notebooks in a directory | ||||
GET with path and notebook name | ||||
GET get checks if a notebook is not named, an returns a list of notebooks | ||||
|
r13036 | in the notebook path given. If a name is given, return | ||
the notebook representation""" | ||||
|
r10641 | nbm = self.notebook_manager | ||
|
r13036 | # Check to see if a notebook name was given | ||
if name is None: | ||||
|
r13067 | # List notebooks in 'path' | ||
|
r12997 | notebooks = nbm.list_notebooks(path) | ||
|
r13045 | self.finish(json.dumps(notebooks, default=date_default)) | ||
|
r12984 | else: | ||
|
r13036 | # get and return notebook representation | ||
|
r13045 | model = nbm.get_notebook_model(name, path) | ||
self.set_header(u'Last-Modified', model[u'last_modified']) | ||||
self.finish(json.dumps(model, default=date_default)) | ||||
|
r10641 | |||
@web.authenticated | ||||
|
r13067 | @json_errors | ||
def patch(self, path='', name=None): | ||||
|
r13074 | """PATCH renames a notebook without re-uploading content.""" | ||
|
r12997 | nbm = self.notebook_manager | ||
|
r13045 | if name is None: | ||
raise web.HTTPError(400, u'Notebook name missing') | ||||
model = self.get_json_body() | ||||
if model is None: | ||||
raise web.HTTPError(400, u'JSON body missing') | ||||
model = nbm.update_notebook_model(model, name, path) | ||||
if model[u'name'] != name or model[u'path'] != path: | ||||
self.set_status(301) | ||||
location = self.notebook_location(model[u'name'], model[u'path']) | ||||
self.set_header(u'Location', location) | ||||
self.set_header(u'Last-Modified', model[u'last_modified']) | ||||
self.finish(json.dumps(model, default=date_default)) | ||||
|
r12997 | |||
@web.authenticated | ||||
|
r13045 | @json_errors | ||
|
r13067 | def post(self, path='', name=None): | ||
|
r13074 | """Create a new notebook in the specified path. | ||
POST creates new notebooks. | ||||
POST /api/notebooks/path : new untitled notebook in path | ||||
POST /api/notebooks/path/notebook.ipynb : new notebook with name in path | ||||
If content specified upload notebook, otherwise start empty. | ||||
""" | ||||
|
r10641 | nbm = self.notebook_manager | ||
|
r13045 | model = self.get_json_body() | ||
|
r13074 | if name is None: | ||
# creating new notebook, model doesn't make sense | ||||
if model is not None: | ||||
raise web.HTTPError(400, "Model not valid when creating untitled notebooks.") | ||||
model = nbm.create_notebook_model(path=path) | ||||
else: | ||||
if model is None: | ||||
self.log.info("Creating new Notebook at %s/%s", path, name) | ||||
model = {} | ||||
else: | ||||
self.log.info("Uploading Notebook to %s/%s", path, name) | ||||
# set the model name from the URL | ||||
model['name'] = name | ||||
model = nbm.create_notebook_model(model, path) | ||||
|
r13045 | location = self.notebook_location(model[u'name'], model[u'path']) | ||
self.set_header(u'Location', location) | ||||
self.set_header(u'Last-Modified', model[u'last_modified']) | ||||
self.set_status(201) | ||||
self.finish(json.dumps(model, default=date_default)) | ||||
|
r13036 | |||
@web.authenticated | ||||
|
r13045 | @json_errors | ||
|
r13067 | def put(self, path='', name=None): | ||
|
r13036 | """saves the notebook in the location given by 'notebook_path'.""" | ||
nbm = self.notebook_manager | ||||
|
r13045 | model = self.get_json_body() | ||
if model is None: | ||||
raise web.HTTPError(400, u'JSON body missing') | ||||
nbm.save_notebook_model(model, name, path) | ||||
self.finish(json.dumps(model, default=date_default)) | ||||
|
r10641 | |||
@web.authenticated | ||||
|
r13045 | @json_errors | ||
|
r13067 | def delete(self, path='', name=None): | ||
|
r13045 | """delete the notebook in the given notebook path""" | ||
|
r12984 | nbm = self.notebook_manager | ||
|
r13045 | nbm.delete_notebook_model(name, path) | ||
|
r10641 | self.set_status(204) | ||
self.finish() | ||||
|
r13074 | class NotebookCopyHandler(IPythonHandler): | ||
SUPPORTED_METHODS = ('POST') | ||||
@web.authenticated | ||||
@json_errors | ||||
def post(self, path='', name=None): | ||||
"""Copy an existing notebook.""" | ||||
nbm = self.notebook_manager | ||||
model = self.get_json_body() | ||||
if name is None: | ||||
raise web.HTTPError(400, "Notebook name required") | ||||
self.log.info("Copying Notebook %s/%s", path, name) | ||||
model = nbm.copy_notebook(name, path) | ||||
location = url_path_join( | ||||
self.base_project_url, 'api', 'notebooks', | ||||
model['path'], model['name'], | ||||
) | ||||
self.set_header(u'Location', location) | ||||
self.set_header(u'Last-Modified', model[u'last_modified']) | ||||
self.set_status(201) | ||||
self.finish(json.dumps(model, default=date_default)) | ||||
|
r10641 | |||
class NotebookCheckpointsHandler(IPythonHandler): | ||||
SUPPORTED_METHODS = ('GET', 'POST') | ||||
@web.authenticated | ||||
|
r13045 | @json_errors | ||
|
r13067 | def get(self, path='', name=None): | ||
|
r10641 | """get lists checkpoints for a notebook""" | ||
nbm = self.notebook_manager | ||||
|
r12984 | checkpoints = nbm.list_checkpoints(name, path) | ||
|
r13045 | data = json.dumps(checkpoints, default=date_default) | ||
|
r10641 | self.finish(data) | ||
@web.authenticated | ||||
|
r13045 | @json_errors | ||
|
r13067 | def post(self, path='', name=None): | ||
|
r10641 | """post creates a new checkpoint""" | ||
nbm = self.notebook_manager | ||||
|
r12984 | checkpoint = nbm.create_checkpoint(name, path) | ||
|
r13045 | data = json.dumps(checkpoint, default=date_default) | ||
location = url_path_join(self.base_project_url, u'/api/notebooks', | ||||
|
r13067 | path, name, 'checkpoints', checkpoint[u'checkpoint_id']) | ||
|
r13045 | self.set_header(u'Location', location) | ||
|
r10641 | self.finish(data) | ||
class ModifyNotebookCheckpointsHandler(IPythonHandler): | ||||
SUPPORTED_METHODS = ('POST', 'DELETE') | ||||
@web.authenticated | ||||
|
r13045 | @json_errors | ||
|
r13067 | def post(self, path, name, checkpoint_id): | ||
|
r10641 | """post restores a notebook from a checkpoint""" | ||
nbm = self.notebook_manager | ||||
|
r13045 | nbm.restore_checkpoint(checkpoint_id, name, path) | ||
|
r10641 | self.set_status(204) | ||
self.finish() | ||||
@web.authenticated | ||||
|
r13045 | @json_errors | ||
|
r13067 | def delete(self, path, name, checkpoint_id): | ||
|
r10641 | """delete clears a checkpoint for a given notebook""" | ||
nbm = self.notebook_manager | ||||
|
r13045 | nbm.delete_checkpoint(checkpoint_id, name, path) | ||
|
r10641 | self.set_status(204) | ||
self.finish() | ||||
|
r12984 | |||
|
r10647 | #----------------------------------------------------------------------------- | ||
# URL to handler mappings | ||||
#----------------------------------------------------------------------------- | ||||
|
r13067 | _path_regex = r"(?P<path>.*)" | ||
|
r10647 | _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)" | ||
|
r13067 | _notebook_name_regex = r"(?P<name>[^/]+\.ipynb)" | ||
_notebook_path_regex = "%s/%s" % (_path_regex, _notebook_name_regex) | ||||
|
r10647 | |||
default_handlers = [ | ||||
|
r13074 | (r"/api/notebooks/?%s/copy" % _notebook_path_regex, NotebookCopyHandler), | ||
|
r13067 | (r"/api/notebooks/?%s/checkpoints" % _notebook_path_regex, NotebookCheckpointsHandler), | ||
(r"/api/notebooks/?%s/checkpoints/%s" % (_notebook_path_regex, _checkpoint_id_regex), | ||||
|
r12984 | ModifyNotebookCheckpointsHandler), | ||
|
r13067 | (r"/api/notebooks/?%s" % _notebook_path_regex, NotebookHandler), | ||
(r"/api/notebooks/?%s/?" % _path_regex, NotebookHandler), | ||||
|
r10647 | ] | ||
|
r10641 | |||