##// END OF EJS Templates
Markdown cells are now saved and restored in notebooks.
Markdown cells are now saved and restored in notebooks.

File last commit:

r4494:03eb23cd
r4511:a85c3e74
Show More
notebookmanager.py
225 lines | 7.8 KiB | text/x-python | PythonLexer
Brian E. Granger
Massive work on the notebook document format....
r4484 #-----------------------------------------------------------------------------
# Copyright (C) 2011 The IPython Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING.txt, distributed as part of this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
import datetime
import os
import uuid
from tornado import web
Brian E. Granger
Adding kernel/notebook associations.
r4494 from IPython.config.configurable import LoggingConfigurable
Brian E. Granger
Massive work on the notebook document format....
r4484 from IPython.nbformat import current
from IPython.utils.traitlets import Unicode, List, Dict
#-----------------------------------------------------------------------------
# Code
#-----------------------------------------------------------------------------
Brian E. Granger
Adding kernel/notebook associations.
r4494 class NotebookManager(LoggingConfigurable):
Brian E. Granger
Massive work on the notebook document format....
r4484
notebook_dir = Unicode(os.getcwd())
filename_ext = Unicode(u'.ipynb')
allowed_formats = List([u'json',u'xml',u'py'])
# Map notebook_ids to notebook names
mapping = Dict()
# Map notebook names to notebook_ids
rev_mapping = Dict()
def list_notebooks(self):
"""List all notebooks in the notebook dir.
This returns a list of dicts of the form::
dict(notebook_id=notebook,name=name)
"""
names = os.listdir(self.notebook_dir)
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 names = [name.split(u'.')[0] \
Brian E. Granger
Massive work on the notebook document format....
r4484 for name in names if name.endswith(self.filename_ext)]
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))
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 data = sorted(data, key=lambda item: item['name'])
Brian E. Granger
Massive work on the notebook document format....
r4484 return data
def new_notebook_id(self, name):
"""Generate a new notebook_id for a name and store its mappings."""
notebook_id = unicode(uuid.uuid4())
self.mapping[notebook_id] = name
self.rev_mapping[name] = notebook_id
return notebook_id
def delete_notebook_id(self, notebook_id):
"""Delete a notebook's id only. This doesn't delete the actual notebook."""
name = self.mapping[notebook_id]
del self.mapping[notebook_id]
del self.rev_mapping[name]
def notebook_exists(self, notebook_id):
"""Does a notebook exist?"""
if notebook_id not in self.mapping:
return False
path = self.get_path_by_name(self.mapping[notebook_id])
if not os.path.isfile(path):
return False
return True
def find_path(self, notebook_id):
"""Return a full path to a notebook given its notebook_id."""
try:
name = self.mapping[notebook_id]
except KeyError:
raise web.HTTPError(404)
return self.get_path_by_name(name)
def get_path_by_name(self, name):
"""Return a full path to a notebook given its name."""
filename = name + self.filename_ext
path = os.path.join(self.notebook_dir, filename)
return path
def get_notebook(self, notebook_id, format=u'json'):
"""Get the representation of a notebook in format by notebook_id."""
format = unicode(format)
if format not in self.allowed_formats:
raise web.HTTPError(415)
last_modified, nb = self.get_notebook_object(notebook_id)
data = current.writes(nb, format)
name = nb.get('name','notebook')
return last_modified, name, data
def get_notebook_object(self, notebook_id):
"""Get the NotebookNode representation of a notebook by notebook_id."""
path = self.find_path(notebook_id)
if not os.path.isfile(path):
raise web.HTTPError(404)
info = os.stat(path)
last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
try:
with open(path,'r') as f:
s = f.read()
try:
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 # v2 and later have xml in the .ipynb files.
Brian E. Granger
Massive work on the notebook document format....
r4484 nb = current.reads(s, 'xml')
except:
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 # v1 had json in the .ipynb files.
Brian E. Granger
Massive work on the notebook document format....
r4484 nb = current.reads(s, 'json')
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 # v1 notebooks don't have a name field, so use the filename.
nb.name = os.path.split(path)[-1].split(u'.')[0]
Brian E. Granger
Massive work on the notebook document format....
r4484 except:
raise web.HTTPError(404)
return last_modified, nb
Brian E. Granger
File upload/import working from notebook browser.
r4491 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.
"""
Brian E. Granger
Massive work on the notebook document format....
r4484 if format not in self.allowed_formats:
raise web.HTTPError(415)
Brian E. Granger
File upload/import working from notebook browser.
r4491
Brian E. Granger
Massive work on the notebook document format....
r4484 try:
nb = current.reads(data, format)
except:
Brian E. Granger
File upload/import working from notebook browser.
r4491 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
Brian E. Granger
Massive work on the notebook document format....
r4484 notebook_id = self.new_notebook_id(name)
self.save_notebook_object(notebook_id, nb)
return notebook_id
Brian E. Granger
File upload/import working from notebook browser.
r4491 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
Brian E. Granger
Massive work on the notebook document format....
r4484 """Save an existing notebook by notebook_id."""
if format not in self.allowed_formats:
raise web.HTTPError(415)
Brian E. Granger
File upload/import working from notebook browser.
r4491
Brian E. Granger
Massive work on the notebook document format....
r4484 try:
nb = current.reads(data, format)
except:
Brian E. Granger
File upload/import working from notebook browser.
r4491 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
Brian E. Granger
Massive work on the notebook document format....
r4484 self.save_notebook_object(notebook_id, nb)
def save_notebook_object(self, notebook_id, nb):
"""Save an existing notebook object by notebook_id."""
if notebook_id not in self.mapping:
raise web.HTTPError(404)
old_name = self.mapping[notebook_id]
try:
new_name = nb.name
except AttributeError:
raise web.HTTPError(400)
path = self.get_path_by_name(new_name)
try:
with open(path,'w') as f:
current.write(nb, f, u'xml')
except:
raise web.HTTPError(400)
if old_name != new_name:
old_path = self.get_path_by_name(old_name)
if os.path.isfile(old_path):
os.unlink(old_path)
self.mapping[notebook_id] = new_name
self.rev_mapping[new_name] = notebook_id
def delete_notebook(self, notebook_id):
"""Delete notebook by notebook_id."""
path = self.find_path(notebook_id)
if not os.path.isfile(path):
raise web.HTTPError(404)
os.unlink(path)
self.delete_notebook_id(notebook_id)
def new_notebook(self):
"""Create a new notebook and returns its notebook_id."""
i = 0
while True:
name = u'Untitled%i' % i
path = self.get_path_by_name(name)
if not os.path.isfile(path):
break
else:
i = i+1
notebook_id = self.new_notebook_id(name)
nb = current.new_notebook(name=name, id=notebook_id)
with open(path,'w') as f:
current.write(nb, f, u'xml')
return notebook_id