##// END OF EJS Templates
Fixing minor bug.
Brian Granger -
Show More
@@ -1,196 +1,196 b''
1 """A notebook manager that uses the local file system for storage.
1 """A notebook manager that uses the local file system for storage.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
9 # Copyright (C) 2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import datetime
19 import datetime
20 import io
20 import io
21 import os
21 import os
22 import glob
22 import glob
23
23
24 from tornado import web
24 from tornado import web
25
25
26 from .basenbmanager import BaseNotebookManager
26 from .basenbmanager import BaseNotebookManager
27 from IPython.nbformat import current
27 from IPython.nbformat import current
28 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
28 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Classes
31 # Classes
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 class FileNotebookManager(BaseNotebookManager):
34 class FileNotebookManager(BaseNotebookManager):
35
35
36 save_script = Bool(False, config=True,
36 save_script = Bool(False, config=True,
37 help="""Automatically create a Python script when saving the notebook.
37 help="""Automatically create a Python script when saving the notebook.
38
38
39 For easier use of import, %run and %load across notebooks, a
39 For easier use of import, %run and %load across notebooks, a
40 <notebook-name>.py script will be created next to any
40 <notebook-name>.py script will be created next to any
41 <notebook-name>.ipynb on each save. This can also be set with the
41 <notebook-name>.ipynb on each save. This can also be set with the
42 short `--script` flag.
42 short `--script` flag.
43 """
43 """
44 )
44 )
45
45
46 filename_ext = Unicode(u'.ipynb')
46 filename_ext = Unicode(u'.ipynb')
47
47
48 # Map notebook names to notebook_ids
48 # Map notebook names to notebook_ids
49 rev_mapping = Dict()
49 rev_mapping = Dict()
50
50
51 def get_notebook_names(self):
51 def get_notebook_names(self):
52 """List all notebook names in the notebook dir."""
52 """List all notebook names in the notebook dir."""
53 names = glob.glob(os.path.join(self.notebook_dir,
53 names = glob.glob(os.path.join(self.notebook_dir,
54 '*' + self.filename_ext))
54 '*' + self.filename_ext))
55 names = [os.path.splitext(os.path.basename(name))[0]
55 names = [os.path.splitext(os.path.basename(name))[0]
56 for name in names]
56 for name in names]
57 return names
57 return names
58
58
59 def list_notebooks(self):
59 def list_notebooks(self):
60 """List all notebooks in the notebook dir."""
60 """List all notebooks in the notebook dir."""
61 names = self.get_notebook_names()
61 names = self.get_notebook_names()
62
62
63 data = []
63 data = []
64 for name in names:
64 for name in names:
65 if name not in self.rev_mapping:
65 if name not in self.rev_mapping:
66 notebook_id = self.new_notebook_id(name)
66 notebook_id = self.new_notebook_id(name)
67 else:
67 else:
68 notebook_id = self.rev_mapping[name]
68 notebook_id = self.rev_mapping[name]
69 data.append(dict(notebook_id=notebook_id,name=name))
69 data.append(dict(notebook_id=notebook_id,name=name))
70 data = sorted(data, key=lambda item: item['name'])
70 data = sorted(data, key=lambda item: item['name'])
71 return data
71 return data
72
72
73 def new_notebook_id(self, name):
73 def new_notebook_id(self, name):
74 """Generate a new notebook_id for a name and store its mappings."""
74 """Generate a new notebook_id for a name and store its mappings."""
75 notebook_id = super(FileNotebookManager, self).new_notebook_id(name)
75 notebook_id = super(FileNotebookManager, self).new_notebook_id(name)
76 self.rev_mapping[name] = notebook_id
76 self.rev_mapping[name] = notebook_id
77 return notebook_id
77 return notebook_id
78
78
79 def delete_notebook_id(self, notebook_id):
79 def delete_notebook_id(self, notebook_id):
80 """Delete a notebook's id in the mapping."""
80 """Delete a notebook's id in the mapping."""
81 name = self.mapping[notebook_id]
81 name = self.mapping[notebook_id]
82 super(FileNotebookManager, self).delete_notebook_id(notebook_id)
82 super(FileNotebookManager, self).delete_notebook_id(notebook_id)
83 del self.rev_mapping[name]
83 del self.rev_mapping[name]
84
84
85 def notebook_exists(self, notebook_id):
85 def notebook_exists(self, notebook_id):
86 """Does a notebook exist?"""
86 """Does a notebook exist?"""
87 exists = super(FileNotebookManager, self).notebook_exists(notebook_id)
87 exists = super(FileNotebookManager, self).notebook_exists(notebook_id)
88 if not exists:
88 if not exists:
89 return False
89 return False
90 path = self.get_path_by_name(self.mapping[notebook_id])
90 path = self.get_path_by_name(self.mapping[notebook_id])
91 return os.path.isfile(path)
91 return os.path.isfile(path)
92
92
93 def find_path(self, notebook_id):
93 def find_path(self, notebook_id):
94 """Return a full path to a notebook given its notebook_id."""
94 """Return a full path to a notebook given its notebook_id."""
95 try:
95 try:
96 name = self.mapping[notebook_id]
96 name = self.mapping[notebook_id]
97 except KeyError:
97 except KeyError:
98 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
98 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
99 return self.get_path_by_name(name)
99 return self.get_path_by_name(name)
100
100
101 def get_path_by_name(self, name):
101 def get_path_by_name(self, name):
102 """Return a full path to a notebook given its name."""
102 """Return a full path to a notebook given its name."""
103 filename = name + self.filename_ext
103 filename = name + self.filename_ext
104 path = os.path.join(self.notebook_dir, filename)
104 path = os.path.join(self.notebook_dir, filename)
105 return path
105 return path
106
106
107 def read_notebook_object(self, notebook_id):
107 def read_notebook_object(self, notebook_id):
108 """Get the NotebookNode representation of a notebook by notebook_id."""
108 """Get the NotebookNode representation of a notebook by notebook_id."""
109 path = self.find_path(notebook_id)
109 path = self.find_path(notebook_id)
110 if not os.path.isfile(path):
110 if not os.path.isfile(path):
111 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
111 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
112 info = os.stat(path)
112 info = os.stat(path)
113 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
113 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
114 with open(path,'r') as f:
114 with open(path,'r') as f:
115 s = f.read()
115 s = f.read()
116 try:
116 try:
117 # v1 and v2 and json in the .ipynb files.
117 # v1 and v2 and json in the .ipynb files.
118 nb = current.reads(s, u'json')
118 nb = current.reads(s, u'json')
119 except:
119 except:
120 raise web.HTTPError(500, u'Unreadable JSON notebook.')
120 raise web.HTTPError(500, u'Unreadable JSON notebook.')
121 # Always use the filename as the notebook name.
121 # Always use the filename as the notebook name.
122 nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
122 nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
123 return last_modified, nb
123 return last_modified, nb
124
124
125 def write_notebook_object(self, nb, notebook_id=None):
125 def write_notebook_object(self, nb, notebook_id=None):
126 """Save an existing notebook object by notebook_id."""
126 """Save an existing notebook object by notebook_id."""
127 try:
127 try:
128 new_name = nb.metadata.name
128 new_name = nb.metadata.name
129 except AttributeError:
129 except AttributeError:
130 raise web.HTTPError(400, u'Missing notebook name')
130 raise web.HTTPError(400, u'Missing notebook name')
131
131
132 if notebook_id is None:
132 if notebook_id is None:
133 notebook_id = self.new_notebook_id(new_name)
133 notebook_id = self.new_notebook_id(new_name)
134
134
135 if notebook_id not in self.mapping:
135 if notebook_id not in self.mapping:
136 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
136 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
137
137
138 old_name = self.mapping[notebook_id]
138 old_name = self.mapping[notebook_id]
139 path = self.get_path_by_name(new_name)
139 path = self.get_path_by_name(new_name)
140 try:
140 try:
141 with open(path,'w') as f:
141 with open(path,'w') as f:
142 current.write(nb, f, u'json')
142 current.write(nb, f, u'json')
143 except Exception as e:
143 except Exception as e:
144 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
144 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
145
145
146 # save .py script as well
146 # save .py script as well
147 if self.save_script:
147 if self.save_script:
148 pypath = os.path.splitext(path)[0] + '.py'
148 pypath = os.path.splitext(path)[0] + '.py'
149 try:
149 try:
150 with io.open(pypath,'w', encoding='utf-8') as f:
150 with io.open(pypath,'w', encoding='utf-8') as f:
151 current.write(nb, f, u'py')
151 current.write(nb, f, u'py')
152 except Exception as e:
152 except Exception as e:
153 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
153 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
154
154
155 # remove old files if the name changed
155 # remove old files if the name changed
156 if old_name != new_name:
156 if old_name != new_name:
157 old_path = self.get_path_by_name(old_name)
157 old_path = self.get_path_by_name(old_name)
158 if os.path.isfile(old_path):
158 if os.path.isfile(old_path):
159 os.unlink(old_path)
159 os.unlink(old_path)
160 if self.save_script:
160 if self.save_script:
161 old_pypath = os.path.splitext(old_path)[0] + '.py'
161 old_pypath = os.path.splitext(old_path)[0] + '.py'
162 if os.path.isfile(old_pypath):
162 if os.path.isfile(old_pypath):
163 os.unlink(old_pypath)
163 os.unlink(old_pypath)
164 self.mapping[notebook_id] = new_name
164 self.mapping[notebook_id] = new_name
165 self.rev_mapping[new_name] = notebook_id
165 self.rev_mapping[new_name] = notebook_id
166 del self.rev_mapping[old_name]
166 del self.rev_mapping[old_name]
167
167
168 return notebook_id
168 return notebook_id
169
169
170 def delete_notebook(self, notebook_id):
170 def delete_notebook(self, notebook_id):
171 """Delete notebook by notebook_id."""
171 """Delete notebook by notebook_id."""
172 path = self.find_path(notebook_id)
172 path = self.find_path(notebook_id)
173 if not os.path.isfile(path):
173 if not os.path.isfile(path):
174 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
174 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
175 os.unlink(path)
175 os.unlink(path)
176 self.delete_notebook_id(notebook_id)
176 self.delete_notebook_id(notebook_id)
177
177
178 def increment_filename(self, basename):
178 def increment_filename(self, basename):
179 """Return a non-used filename of the form basename<int>.
179 """Return a non-used filename of the form basename<int>.
180
180
181 This searches through the filenames (basename0, basename1, ...)
181 This searches through the filenames (basename0, basename1, ...)
182 until is find one that is not already being used. It is used to
182 until is find one that is not already being used. It is used to
183 create Untitled and Copy names that are unique.
183 create Untitled and Copy names that are unique.
184 """
184 """
185 i = 0
185 i = 0
186 while True:
186 while True:
187 name = u'%s%i' % (basename,i)
187 name = u'%s%i' % (basename,i)
188 path = self.get_path_by_name(name)
188 path = self.get_path_by_name(name)
189 if not os.path.isfile(path):
189 if not os.path.isfile(path):
190 break
190 break
191 else:
191 else:
192 i = i+1
192 i = i+1
193 return name
193 return name
194
194
195 def log_info(self):
195 def log_info(self):
196 self.log.info("Serving notebooks from local directory: %s", self.notebook_manager.notebook_dir)
196 self.log.info("Serving notebooks from local directory: %s", self.notebook_dir)
General Comments 0
You need to be logged in to leave comments. Login now