diff --git a/IPython/html/services/notebooks/filenbmanager.py b/IPython/html/services/notebooks/filenbmanager.py
index ddb6835..a9f2480 100644
--- a/IPython/html/services/notebooks/filenbmanager.py
+++ b/IPython/html/services/notebooks/filenbmanager.py
@@ -21,6 +21,7 @@ import io
import os
import glob
import shutil
+
from unicodedata import normalize
from tornado import web
@@ -73,68 +74,47 @@ class FileNotebookManager(NotebookManager):
filename_ext = Unicode(u'.ipynb')
- # Map notebook names to notebook_ids
rev_mapping = Dict()
- def get_notebook_names(self):
+ def get_notebook_names(self, path):
"""List all notebook names in the notebook dir."""
- names = glob.glob(os.path.join(self.notebook_dir,
+ names = glob.glob(os.path.join(self.notebook_dir, path,
'*' + self.filename_ext))
- names = [normalize('NFC', os.path.splitext(os.path.basename(name))[0])
+ #names = [os.path.splitext(os.path.basename(name))[0]
+ names = [os.path.basename(name)
for name in names]
return names
-
- def list_notebooks(self):
+
+ def list_notebooks(self, path):
"""List all notebooks in the notebook dir."""
- names = self.get_notebook_names()
+ names = self.get_notebook_names(path)
data = []
- for name in names:
- if name not in self.rev_mapping:
- notebook_id = self.new_notebook_id(name)
- else:
- notebook_id = self.rev_mapping[name]
- data.append(dict(notebook_id=notebook_id,name=name))
- data = sorted(data, key=lambda item: item['name'])
- return data
-
- def new_notebook_id(self, name):
- """Generate a new notebook_id for a name and store its mappings."""
- notebook_id = super(FileNotebookManager, self).new_notebook_id(name)
- self.rev_mapping[name] = notebook_id
- return notebook_id
-
- def delete_notebook_id(self, notebook_id):
- """Delete a notebook's id in the mapping."""
- name = self.mapping[notebook_id]
- super(FileNotebookManager, self).delete_notebook_id(notebook_id)
- del self.rev_mapping[name]
+ for name in names:
+ data.append(name)
+ #data = sorted(data, key=lambda item: item['name'])
+ return names
- def notebook_exists(self, notebook_id):
+ def notebook_exists(self, notebook_name):
"""Does a notebook exist?"""
- exists = super(FileNotebookManager, self).notebook_exists(notebook_id)
+ exists = super(FileNotebookManager, self).notebook_exists(notebook_name)
if not exists:
return False
- path = self.get_path_by_name(self.mapping[notebook_id])
+ path = self.get_path_by_name(self.mapping[notebook_name])
return os.path.isfile(path)
-
- def get_name(self, notebook_id):
- """get a notebook name, raising 404 if not found"""
- try:
- name = self.mapping[notebook_id]
- except KeyError:
- raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
- return name
- def get_path(self, notebook_id):
- """Return a full path to a notebook given its notebook_id."""
- name = self.get_name(notebook_id)
- return self.get_path_by_name(name)
- def get_path_by_name(self, name):
+ def get_path(self, notebook_name, notebook_path=None):
+ """Return a full path to a notebook given its notebook_name."""
+ return self.get_path_by_name(notebook_name, notebook_path)
+
+ def get_path_by_name(self, name, notebook_path=None):
"""Return a full path to a notebook given its name."""
- filename = name + self.filename_ext
- path = os.path.join(self.notebook_dir, filename)
+ filename = name #+ self.filename_ext
+ if notebook_path == None:
+ path = os.path.join(self.notebook_dir, filename)
+ else:
+ path = os.path.join(self.notebook_dir, notebook_path, filename)
return path
def read_notebook_object_from_path(self, path):
@@ -151,11 +131,11 @@ class FileNotebookManager(NotebookManager):
raise web.HTTPError(400, msg, reason=msg)
return last_modified, nb
- def read_notebook_object(self, notebook_id):
- """Get the Notebook representation of a notebook by notebook_id."""
- path = self.get_path(notebook_id)
+ def read_notebook_object(self, notebook_name, notebook_path):
+ """Get the Notebook representation of a notebook by notebook_name."""
+ path = self.get_path(notebook_name, notebook_path)
if not os.path.isfile(path):
- raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
+ raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_name)
last_modified, nb = self.read_notebook_object_from_path(path)
# Always use the filename as the notebook name.
# Eventually we will get rid of the notebook name in the metadata
@@ -165,23 +145,20 @@ class FileNotebookManager(NotebookManager):
nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
return last_modified, nb
- def write_notebook_object(self, nb, notebook_id=None):
- """Save an existing notebook object by notebook_id."""
+ def write_notebook_object(self, nb, notebook_name=None, notebook_path=None):
+ """Save an existing notebook object by notebook_name."""
try:
new_name = normalize('NFC', nb.metadata.name)
except AttributeError:
raise web.HTTPError(400, u'Missing notebook name')
-
- if notebook_id is None:
- notebook_id = self.new_notebook_id(new_name)
-
- if notebook_id not in self.mapping:
- raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
-
- old_name = self.mapping[notebook_id]
- old_checkpoints = self.list_checkpoints(notebook_id)
- path = self.get_path_by_name(new_name)
-
+
+ new_path = notebook_path
+ old_name = notebook_name
+# old_name = self.mapping[notebook_name]
+ old_checkpoints = self.list_checkpoints(old_name)
+
+ path = self.get_path_by_name(new_name, new_path)
+
# Right before we save the notebook, we write an empty string as the
# notebook name in the metadata. This is to prepare for removing
# this attribute entirely post 1.0. The web app still uses the metadata
@@ -205,47 +182,43 @@ class FileNotebookManager(NotebookManager):
except Exception as e:
raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
- # remove old files if the name changed
- if old_name != new_name:
- # update mapping
- self.mapping[notebook_id] = new_name
- self.rev_mapping[new_name] = notebook_id
- del self.rev_mapping[old_name]
-
- # remove renamed original, if it exists
- old_path = self.get_path_by_name(old_name)
- if os.path.isfile(old_path):
- self.log.debug("unlinking notebook %s", old_path)
- os.unlink(old_path)
+ if old_name != None:
+ # remove old files if the name changed
+ if old_name != new_name:
+ # remove renamed original, if it exists
+ old_path = self.get_path_by_name(old_name, notebook_path)
+ if os.path.isfile(old_path):
+ self.log.debug("unlinking notebook %s", old_path)
+ os.unlink(old_path)
- # cleanup old script, if it exists
- if self.save_script:
- old_pypath = os.path.splitext(old_path)[0] + '.py'
- if os.path.isfile(old_pypath):
- self.log.debug("unlinking script %s", old_pypath)
- os.unlink(old_pypath)
+ # cleanup old script, if it exists
+ if self.save_script:
+ old_pypath = os.path.splitext(old_path)[0] + '.py'
+ if os.path.isfile(old_pypath):
+ self.log.debug("unlinking script %s", old_pypath)
+ os.unlink(old_pypath)
+
+ # rename checkpoints to follow file
+ for cp in old_checkpoints:
+ checkpoint_id = cp['checkpoint_id']
+ old_cp_path = self.get_checkpoint_path_by_name(old_name, checkpoint_id)
+ new_cp_path = self.get_checkpoint_path_by_name(new_name, checkpoint_id)
+ if os.path.isfile(old_cp_path):
+ self.log.debug("renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
+ os.rename(old_cp_path, new_cp_path)
- # rename checkpoints to follow file
- for cp in old_checkpoints:
- checkpoint_id = cp['checkpoint_id']
- old_cp_path = self.get_checkpoint_path_by_name(old_name, checkpoint_id)
- new_cp_path = self.get_checkpoint_path_by_name(new_name, checkpoint_id)
- if os.path.isfile(old_cp_path):
- self.log.debug("renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
- os.rename(old_cp_path, new_cp_path)
+ return new_name
- return notebook_id
-
- def delete_notebook(self, notebook_id):
- """Delete notebook by notebook_id."""
- nb_path = self.get_path(notebook_id)
+ def delete_notebook(self, notebook_name, notebook_path):
+ """Delete notebook by notebook_name."""
+ nb_path = self.get_path(notebook_name, notebook_path)
if not os.path.isfile(nb_path):
- raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
+ raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_name)
# clear checkpoints
- for checkpoint in self.list_checkpoints(notebook_id):
+ for checkpoint in self.list_checkpoints(notebook_name):
checkpoint_id = checkpoint['checkpoint_id']
- path = self.get_checkpoint_path(notebook_id, checkpoint_id)
+ path = self.get_checkpoint_path(notebook_name, checkpoint_id)
self.log.debug(path)
if os.path.isfile(path):
self.log.debug("unlinking checkpoint %s", path)
@@ -253,9 +226,8 @@ class FileNotebookManager(NotebookManager):
self.log.debug("unlinking notebook %s", nb_path)
os.unlink(nb_path)
- self.delete_notebook_id(notebook_id)
- def increment_filename(self, basename):
+ def increment_filename(self, basename, notebook_path=None):
"""Return a non-used filename of the form basename.
This searches through the filenames (basename0, basename1, ...)
@@ -264,8 +236,8 @@ class FileNotebookManager(NotebookManager):
"""
i = 0
while True:
- name = u'%s%i' % (basename,i)
- path = self.get_path_by_name(name)
+ name = u'%s%i.ipynb' % (basename,i)
+ path = self.get_path_by_name(name, notebook_path)
if not os.path.isfile(path):
break
else:
@@ -274,24 +246,27 @@ class FileNotebookManager(NotebookManager):
# Checkpoint-related utilities
- def get_checkpoint_path_by_name(self, name, checkpoint_id):
+ def get_checkpoint_path_by_name(self, name, checkpoint_id, notebook_path=None):
"""Return a full path to a notebook checkpoint, given its name and checkpoint id."""
filename = u"{name}-{checkpoint_id}{ext}".format(
name=name,
checkpoint_id=checkpoint_id,
ext=self.filename_ext,
)
- path = os.path.join(self.checkpoint_dir, filename)
+ if notebook_path ==None:
+ path = os.path.join(self.checkpoint_dir, filename)
+ else:
+ path = os.path.join(notebook_path, self.checkpoint_dir, filename)
return path
- def get_checkpoint_path(self, notebook_id, checkpoint_id):
+ def get_checkpoint_path(self, notebook_name, checkpoint_id, notebook_path=None):
"""find the path to a checkpoint"""
- name = self.get_name(notebook_id)
- return self.get_checkpoint_path_by_name(name, checkpoint_id)
+ name = notebook_name
+ return self.get_checkpoint_path_by_name(name, checkpoint_id, notebook_path)
- def get_checkpoint_info(self, notebook_id, checkpoint_id):
+ def get_checkpoint_info(self, notebook_name, checkpoint_id, notebook_path=None):
"""construct the info dict for a given checkpoint"""
- path = self.get_checkpoint_path(notebook_id, checkpoint_id)
+ path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
stats = os.stat(path)
last_modified = tz.utcfromtimestamp(stats.st_mtime)
info = dict(
@@ -303,54 +278,54 @@ class FileNotebookManager(NotebookManager):
# public checkpoint API
- def create_checkpoint(self, notebook_id):
+ def create_checkpoint(self, notebook_name, notebook_path=None):
"""Create a checkpoint from the current state of a notebook"""
- nb_path = self.get_path(notebook_id)
+ nb_path = self.get_path(notebook_name, notebook_path)
# only the one checkpoint ID:
checkpoint_id = u"checkpoint"
- cp_path = self.get_checkpoint_path(notebook_id, checkpoint_id)
- self.log.debug("creating checkpoint for notebook %s", notebook_id)
+ cp_path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
+ self.log.debug("creating checkpoint for notebook %s", notebook_name)
if not os.path.exists(self.checkpoint_dir):
os.mkdir(self.checkpoint_dir)
shutil.copy2(nb_path, cp_path)
# return the checkpoint info
- return self.get_checkpoint_info(notebook_id, checkpoint_id)
+ return self.get_checkpoint_info(notebook_name, checkpoint_id, notebook_path)
- def list_checkpoints(self, notebook_id):
+ def list_checkpoints(self, notebook_name, notebook_path=None):
"""list the checkpoints for a given notebook
This notebook manager currently only supports one checkpoint per notebook.
"""
- checkpoint_id = u"checkpoint"
- path = self.get_checkpoint_path(notebook_id, checkpoint_id)
+ checkpoint_id = "checkpoint"
+ path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
if not os.path.exists(path):
return []
else:
- return [self.get_checkpoint_info(notebook_id, checkpoint_id)]
+ return [self.get_checkpoint_info(notebook_name, checkpoint_id, notebook_path)]
- def restore_checkpoint(self, notebook_id, checkpoint_id):
+ def restore_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
"""restore a notebook to a checkpointed state"""
- self.log.info("restoring Notebook %s from checkpoint %s", notebook_id, checkpoint_id)
- nb_path = self.get_path(notebook_id)
- cp_path = self.get_checkpoint_path(notebook_id, checkpoint_id)
+ self.log.info("restoring Notebook %s from checkpoint %s", notebook_name, checkpoint_id)
+ nb_path = self.get_path(notebook_name, notebook_path)
+ cp_path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
if not os.path.isfile(cp_path):
self.log.debug("checkpoint file does not exist: %s", cp_path)
raise web.HTTPError(404,
- u'Notebook checkpoint does not exist: %s-%s' % (notebook_id, checkpoint_id)
+ u'Notebook checkpoint does not exist: %s-%s' % (notebook_name, checkpoint_id)
)
# ensure notebook is readable (never restore from an unreadable notebook)
last_modified, nb = self.read_notebook_object_from_path(cp_path)
shutil.copy2(cp_path, nb_path)
self.log.debug("copying %s -> %s", cp_path, nb_path)
- def delete_checkpoint(self, notebook_id, checkpoint_id):
+ def delete_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
"""delete a notebook's checkpoint"""
- path = self.get_checkpoint_path(notebook_id, checkpoint_id)
+ path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
if not os.path.isfile(path):
raise web.HTTPError(404,
- u'Notebook checkpoint does not exist: %s-%s' % (notebook_id, checkpoint_id)
+ u'Notebook checkpoint does not exist: %s-%s' % (notebook_name, checkpoint_id)
)
self.log.debug("unlinking %s", path)
os.unlink(path)
diff --git a/IPython/html/services/notebooks/handlers.py b/IPython/html/services/notebooks/handlers.py
index 30d5cad..e56e09d 100644
--- a/IPython/html/services/notebooks/handlers.py
+++ b/IPython/html/services/notebooks/handlers.py
@@ -28,29 +28,34 @@ from ...base.handlers import IPythonHandler
# Notebook web service handlers
#-----------------------------------------------------------------------------
+
class NotebookRootHandler(IPythonHandler):
@web.authenticated
def get(self):
nbm = self.notebook_manager
km = self.kernel_manager
- files = nbm.list_notebooks()
- for f in files :
- f['kernel_id'] = km.kernel_for_notebook(f['notebook_id'])
- self.finish(jsonapi.dumps(files))
+ notebook_names = nbm.list_notebooks("")
+ notebooks = []
+ for name in notebook_names:
+ model = nbm.notebook_model(name)
+ notebooks.append(model)
+ self.finish(jsonapi.dumps(notebooks))
@web.authenticated
def post(self):
nbm = self.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, name=name, format=format)
- else:
- notebook_id = nbm.new_notebook()
- self.set_header('Location', '{0}notebooks/{1}'.format(self.base_project_url, notebook_id))
- self.finish(jsonapi.dumps(notebook_id))
+ notebook_name = nbm.new_notebook()
+ model = nbm.notebook_model(notebook_name)
+ self.set_header('Location', '{0}api/notebooks/{1}'.format(self.base_project_url, notebook_name))
+ self.finish(jsonapi.dumps(model))
+
+
+class NotebookRootRedirect(IPythonHandler):
+
+ @authenticate_unless_readonly
+ def get(self):
+ self.redirect("/api/notebooks")
class NotebookHandler(IPythonHandler):
@@ -58,32 +63,62 @@ class NotebookHandler(IPythonHandler):
SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
@web.authenticated
- def get(self, notebook_id):
+ def get(self, notebook_path):
nbm = self.notebook_manager
- format = self.get_argument('format', default='json')
- last_mod, name, data = nbm.get_notebook(notebook_id, format)
+ name, path = nbm.named_notebook_path(notebook_path)
- if format == u'json':
- self.set_header('Content-Type', 'application/json')
- self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
- elif format == u'py':
- self.set_header('Content-Type', 'application/x-python')
- self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
- self.set_header('Last-Modified', last_mod)
- self.finish(data)
+ if name == None:
+ notebook_names = nbm.list_notebooks(path)
+ notebooks = []
+ for name in notebook_names:
+ model = nbm.notebook_model(name,path)
+ notebooks.append(model)
+ self.finish(jsonapi.dumps(notebooks))
+ else:
+ format = self.get_argument('format', default='json')
+ model = nbm.notebook_model(name,path)
+ data, name = nbm.get_notebook(model, format)
+
+ if format == u'json':
+ self.set_header('Content-Type', 'application/json')
+ self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
+ elif format == u'py':
+ self.set_header('Content-Type', 'application/x-python')
+ self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
+ #self.set_header('Last-Modified', last_mod)
+ self.finish(jsonapi.dumps(model))
@web.authenticated
- def put(self, notebook_id):
+ def put(self, notebook_path):
nbm = self.notebook_manager
- format = self.get_argument('format', default='json')
- 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()
+ notebook_name, notebook_path = nbm.named_notebook_path(notebook_path)
+ if notebook_name == None:
+ body = self.request.body.strip()
+ format = self.get_argument('format', default='json')
+ name = self.get_argument('name', default=None)
+ if body:
+ notebook_name = nbm.save_new_notebook(body, notebook_path=notebook_path, name=name, format=format)
+ else:
+ notebook_name = nbm.new_notebook(notebook_path=notebook_path)
+ if path==None:
+ self.set_header('Location', nbm.notebook_dir + '/'+ notebook_name)
+ else:
+ self.set_header('Location', nbm.notebook_dir + '/'+ notebook_path + '/' + notebook_name)
+ model = nbm.notebook_model(notebook_name, notebook_path)
+ self.finish(jsonapi.dumps(model))
+ else:
+ format = self.get_argument('format', default='json')
+ name = self.get_argument('name', default=None)
+ nbm.save_notebook(self.request.body, notebook_path=notebook_path, name=name, format=format)
+ model = nbm.notebook_model(notebook_name, notebook_path)
+ self.set_status(204)
+ self.finish(jsonapi.dumps(model))
@web.authenticated
- def delete(self, notebook_id):
- self.notebook_manager.delete_notebook(notebook_id)
+ def delete(self, notebook_path):
+ nbm = self.notebook_manager
+ name, path = nbm.named_notebook_path(notebook_path)
+ self.notebook_manager.delete_notebook(name, path)
self.set_status(204)
self.finish()
@@ -93,23 +128,29 @@ class NotebookCheckpointsHandler(IPythonHandler):
SUPPORTED_METHODS = ('GET', 'POST')
@web.authenticated
- def get(self, notebook_id):
+ def get(self, notebook_path):
"""get lists checkpoints for a notebook"""
nbm = self.notebook_manager
- checkpoints = nbm.list_checkpoints(notebook_id)
+ name, path = nbm.named_notebook_path(notebook_path)
+ checkpoints = nbm.list_checkpoints(name, path)
data = jsonapi.dumps(checkpoints, default=date_default)
self.finish(data)
@web.authenticated
- def post(self, notebook_id):
+ def post(self, notebook_path):
"""post creates a new checkpoint"""
nbm = self.notebook_manager
- checkpoint = nbm.create_checkpoint(notebook_id)
+ name, path = nbm.named_notebook_path(notebook_path)
+ checkpoint = nbm.create_checkpoint(name, path)
data = jsonapi.dumps(checkpoint, default=date_default)
- self.set_header('Location', '{0}notebooks/{1}/checkpoints/{2}'.format(
- self.base_project_url, notebook_id, checkpoint['checkpoint_id']
- ))
-
+ if path == None:
+ self.set_header('Location', '{0}notebooks/{1}/checkpoints/{2}'.format(
+ self.base_project_url, name, checkpoint['checkpoint_id']
+ ))
+ else:
+ self.set_header('Location', '{0}notebooks/{1}/{2}/checkpoints/{3}'.format(
+ self.base_project_url, path, name, checkpoint['checkpoint_id']
+ ))
self.finish(data)
@@ -118,37 +159,38 @@ class ModifyNotebookCheckpointsHandler(IPythonHandler):
SUPPORTED_METHODS = ('POST', 'DELETE')
@web.authenticated
- def post(self, notebook_id, checkpoint_id):
+ def post(self, notebook_path, checkpoint_id):
"""post restores a notebook from a checkpoint"""
nbm = self.notebook_manager
- nbm.restore_checkpoint(notebook_id, checkpoint_id)
+ name, path = nbm.named_notebook_path(notebook_path)
+ nbm.restore_checkpoint(name, checkpoint_id, path)
self.set_status(204)
self.finish()
@web.authenticated
- def delete(self, notebook_id, checkpoint_id):
+ def delete(self, notebook_path, checkpoint_id):
"""delete clears a checkpoint for a given notebook"""
nbm = self.notebook_manager
- nbm.delte_checkpoint(notebook_id, checkpoint_id)
+ name, path = nbm.named_notebook_path(notebook_path)
+ nbm.delete_checkpoint(name, checkpoint_id, path)
self.set_status(204)
self.finish()
-
-
+
#-----------------------------------------------------------------------------
# URL to handler mappings
#-----------------------------------------------------------------------------
-_notebook_id_regex = r"(?P\w+-\w+-\w+-\w+-\w+)"
+_notebook_path_regex = r"(?P.+)"
_checkpoint_id_regex = r"(?P[\w-]+)"
default_handlers = [
- (r"/notebooks", NotebookRootHandler),
- (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
- (r"/notebooks/%s/checkpoints" % _notebook_id_regex, NotebookCheckpointsHandler),
- (r"/notebooks/%s/checkpoints/%s" % (_notebook_id_regex, _checkpoint_id_regex),
- ModifyNotebookCheckpointsHandler
- ),
+ (r"api/notebooks/%s/checkpoints" % _notebook_path_regex, NotebookCheckpointsHandler),
+ (r"api/notebooks/%s/checkpoints/%s" % (_notebook_path_regex, _checkpoint_id_regex),
+ ModifyNotebookCheckpointsHandler),
+ (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler),
+ (r"api/notebooks/", NotebookRootRedirect),
+ (r"api/notebooks", NotebookRootHandler),
]
diff --git a/IPython/html/services/notebooks/nbmanager.py b/IPython/html/services/notebooks/nbmanager.py
index 0a9321f..f6702be 100644
--- a/IPython/html/services/notebooks/nbmanager.py
+++ b/IPython/html/services/notebooks/nbmanager.py
@@ -38,14 +38,37 @@ class NotebookManager(LoggingConfigurable):
# Right now we use this attribute in a number of different places and
# we are going to have to disentangle all of this.
notebook_dir = Unicode(os.getcwdu(), config=True, help="""
- The directory to use for notebooks.
- """)
- def _notebook_dir_changed(self, name, old, new):
+ The directory to use for notebooks.
+ """)
+
+ def named_notebook_path(self, notebook_path):
+
+ l = len(notebook_path)
+ names = notebook_path.split('/')
+ if len(names) > 1:
+ name = names[len(names)-1]
+ if name[(len(name)-6):(len(name))] == ".ipynb":
+ name = name
+ path = notebook_path[0:l-len(name)-1]+'/'
+ else:
+ name = None
+ path = notebook_path+'/'
+ else:
+ name = names[0]
+ if name[(len(name)-6):(len(name))] == ".ipynb":
+ name = name
+ path = None
+ else:
+ name = None
+ path = notebook_path+'/'
+ return name, path
+
+ def _notebook_dir_changed(self, new):
"""do a bit of validation of the notebook dir"""
if not os.path.isabs(new):
# If we receive a non-absolute path, make it absolute.
abs_new = os.path.abspath(new)
- self.notebook_dir = abs_new
+ #self.notebook_dir = os.path.dirname(abs_new)
return
if os.path.exists(new) and not os.path.isdir(new):
raise TraitError("notebook dir %r is not a directory" % new)
@@ -55,20 +78,18 @@ class NotebookManager(LoggingConfigurable):
os.mkdir(new)
except:
raise TraitError("Couldn't create notebook dir %r" % new)
-
+
allowed_formats = List([u'json',u'py'])
- # Map notebook_ids to notebook names
- mapping = Dict()
- def load_notebook_names(self):
+ def load_notebook_names(self, path):
"""Load the notebook names into memory.
This should be called once immediately after the notebook manager
is created to load the existing notebooks into the mapping in
memory.
"""
- self.list_notebooks()
+ self.list_notebooks(path)
def list_notebooks(self):
"""List all notebooks.
@@ -84,52 +105,37 @@ class NotebookManager(LoggingConfigurable):
raise NotImplementedError('must be implemented in a subclass')
- def new_notebook_id(self, name):
- """Generate a new notebook_id for a name and store its mapping."""
- # TODO: the following will give stable urls for notebooks, but unless
- # the notebooks are immediately redirected to their new urls when their
- # filemname changes, nasty inconsistencies result. So for now it's
- # disabled and instead we use a random uuid4() call. But we leave the
- # logic here so that we can later reactivate it, whhen the necessary
- # url redirection code is written.
- #notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
- # 'file://'+self.get_path_by_name(name).encode('utf-8')))
-
- notebook_id = unicode(uuid.uuid4())
- self.mapping[notebook_id] = name
- return notebook_id
-
- def delete_notebook_id(self, notebook_id):
- """Delete a notebook's id in the mapping.
-
- This doesn't delete the actual notebook, only its entry in the mapping.
- """
- del self.mapping[notebook_id]
-
- def notebook_exists(self, notebook_id):
+ def notebook_exists(self, notebook_name):
"""Does a notebook exist?"""
- return notebook_id in self.mapping
-
- def get_notebook(self, notebook_id, format=u'json'):
- """Get the representation of a notebook in format by notebook_id."""
+ return notebook_name in self.mapping
+
+ def notebook_model(self, notebook_name, notebook_path=None):
+ """ Creates the standard notebook model """
+ last_modified, content = self.read_notebook_object(notebook_name, notebook_path)
+ model = {"notebook_name": notebook_name,
+ "notebook_path": notebook_path,
+ "content": content}
+ return model
+
+ def get_notebook(self, body, format=u'json'):
+ """Get the representation of a notebook in format by notebook_name."""
format = unicode(format)
if format not in self.allowed_formats:
raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
- last_modified, nb = self.read_notebook_object(notebook_id)
kwargs = {}
if format == 'json':
# don't split lines for sending over the wire, because it
# should match the Python in-memory format.
kwargs['split_lines'] = False
- data = current.writes(nb, format, **kwargs)
- name = nb.metadata.get('name','notebook')
- return last_modified, name, data
+ representation = current.writes(body, format, **kwargs)
+ name = body['content']['metadata']['name']
+ return representation, name
- def read_notebook_object(self, notebook_id):
+ def read_notebook_object(self, notebook_name, notebook_path):
"""Get the object representation of a notebook by notebook_id."""
raise NotImplementedError('must be implemented in a subclass')
- def save_new_notebook(self, data, name=None, format=u'json'):
+ def save_new_notebook(self, data, notebook_path = None, 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
@@ -150,10 +156,10 @@ class NotebookManager(LoggingConfigurable):
raise web.HTTPError(400, u'Missing notebook name')
nb.metadata.name = name
- notebook_id = self.write_notebook_object(nb)
- return notebook_id
+ notebook_name = self.write_notebook_object(nb, notebook_path=notebook_path)
+ return notebook_name
- def save_notebook(self, notebook_id, data, name=None, format=u'json'):
+ def save_notebook(self, data, notebook_path=None, name=None, format=u'json'):
"""Save an existing notebook by notebook_id."""
if format not in self.allowed_formats:
raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
@@ -165,18 +171,18 @@ class NotebookManager(LoggingConfigurable):
if name is not None:
nb.metadata.name = name
- self.write_notebook_object(nb, notebook_id)
+ self.write_notebook_object(nb, name, notebook_path)
- def write_notebook_object(self, nb, notebook_id=None):
- """Write a notebook object and return its notebook_id.
+ def write_notebook_object(self, nb, notebook_name=None, notebook_path=None):
+ """Write a notebook object and return its notebook_name.
- If notebook_id is None, this method should create a new notebook_id.
- If notebook_id is not None, this method should check to make sure it
+ If notebook_name is None, this method should create a new notebook_name.
+ If notebook_name is not None, this method should check to make sure it
exists and is valid.
"""
raise NotImplementedError('must be implemented in a subclass')
- def delete_notebook(self, notebook_id):
+ def delete_notebook(self, notebook_name, notebook_path):
"""Delete notebook by notebook_id."""
raise NotImplementedError('must be implemented in a subclass')
@@ -189,41 +195,41 @@ class NotebookManager(LoggingConfigurable):
"""
return name
- def new_notebook(self):
+ def new_notebook(self, notebook_path=None):
"""Create a new notebook and return its notebook_id."""
- name = self.increment_filename('Untitled')
+ name = self.increment_filename('Untitled', notebook_path)
metadata = current.new_metadata(name=name)
nb = current.new_notebook(metadata=metadata)
- notebook_id = self.write_notebook_object(nb)
- return notebook_id
+ notebook_name = self.write_notebook_object(nb, notebook_path=notebook_path)
+ return notebook_name
- def copy_notebook(self, notebook_id):
+ def copy_notebook(self, name, path):
"""Copy an existing notebook and return its notebook_id."""
- last_mod, nb = self.read_notebook_object(notebook_id)
+ last_mod, nb = self.read_notebook_object(name, path)
name = nb.metadata.name + '-Copy'
- name = self.increment_filename(name)
+ name = self.increment_filename(name, path)
nb.metadata.name = name
- notebook_id = self.write_notebook_object(nb)
- return notebook_id
+ notebook_name = self.write_notebook_object(nb, notebook_path = path)
+ return notebook_name
# Checkpoint-related
- def create_checkpoint(self, notebook_id):
+ def create_checkpoint(self, notebook_name, notebook_path=None):
"""Create a checkpoint of the current state of a notebook
Returns a checkpoint_id for the new checkpoint.
"""
raise NotImplementedError("must be implemented in a subclass")
- def list_checkpoints(self, notebook_id):
+ def list_checkpoints(self, notebook_name, notebook_path=None):
"""Return a list of checkpoints for a given notebook"""
return []
- def restore_checkpoint(self, notebook_id, checkpoint_id):
+ def restore_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
"""Restore a notebook from one of its checkpoints"""
raise NotImplementedError("must be implemented in a subclass")
- def delete_checkpoint(self, notebook_id, checkpoint_id):
+ def delete_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
"""delete a checkpoint for a notebook"""
raise NotImplementedError("must be implemented in a subclass")
@@ -232,4 +238,3 @@ class NotebookManager(LoggingConfigurable):
def info_string(self):
return "Serving notebooks"
-