##// END OF EJS Templates
Fix typo in help string
Fernando Perez -
Show More
@@ -1,264 +1,264
1 1 """A notebook manager that uses the local file system for storage.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import datetime
20 20 import os
21 21 import uuid
22 22 import glob
23 23
24 24 from tornado import web
25 25
26 26 from IPython.config.application import boolean_flag
27 27 from IPython.config.configurable import LoggingConfigurable
28 28 from IPython.nbformat import current
29 29 from IPython.utils.traitlets import Unicode, List, Dict, Bool
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Aliases and Flags
33 33 #-----------------------------------------------------------------------------
34 34
35 35 manager_flags =boolean_flag('script', 'NotebookManager.save_script',
36 36 'Auto-save a .py script everytime the .ipynb notebook is saved',
37 37 'Do not auto-save .py scripts for every notebook')
38 38
39 39 #-----------------------------------------------------------------------------
40 40 # Classes
41 41 #-----------------------------------------------------------------------------
42 42
43 43 class NotebookManager(LoggingConfigurable):
44 44
45 45 notebook_dir = Unicode(os.getcwd(), config=True, help="""
46 46 The directory to use for notebooks.
47 47 """)
48 48
49 49 save_script = Bool(False, config=True,
50 help="""Automaticall create a Python script when saving the notebook.
50 help="""Automatically create a Python script when saving the notebook.
51 51
52 52 For easier use of import, %run and %loadpy across notebooks, a
53 53 <notebook-name>.py script will be created next to any
54 54 <notebook-name>.ipynb on each save. This can also be set with the
55 55 short `--script` flag.
56 56 """
57 57 )
58 58
59 59 filename_ext = Unicode(u'.ipynb')
60 60 allowed_formats = List([u'json',u'py'])
61 61
62 62 # Map notebook_ids to notebook names
63 63 mapping = Dict()
64 64 # Map notebook names to notebook_ids
65 65 rev_mapping = Dict()
66 66
67 67 def list_notebooks(self):
68 68 """List all notebooks in the notebook dir.
69 69
70 70 This returns a list of dicts of the form::
71 71
72 72 dict(notebook_id=notebook,name=name)
73 73 """
74 74 names = glob.glob(os.path.join(self.notebook_dir,
75 75 '*' + self.filename_ext))
76 76 names = [os.path.splitext(os.path.basename(name))[0]
77 77 for name in names]
78 78
79 79 data = []
80 80 for name in names:
81 81 if name not in self.rev_mapping:
82 82 notebook_id = self.new_notebook_id(name)
83 83 else:
84 84 notebook_id = self.rev_mapping[name]
85 85 data.append(dict(notebook_id=notebook_id,name=name))
86 86 data = sorted(data, key=lambda item: item['name'])
87 87 return data
88 88
89 89 def new_notebook_id(self, name):
90 90 """Generate a new notebook_id for a name and store its mappings."""
91 91 # TODO: the following will give stable urls for notebooks, but unless
92 92 # the notebooks are immediately redirected to their new urls when their
93 93 # filemname changes, nasty inconsistencies result. So for now it's
94 94 # disabled and instead we use a random uuid4() call. But we leave the
95 95 # logic here so that we can later reactivate it, whhen the necessary
96 96 # url redirection code is written.
97 97 #notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
98 98 # 'file://'+self.get_path_by_name(name).encode('utf-8')))
99 99
100 100 notebook_id = unicode(uuid.uuid4())
101 101
102 102 self.mapping[notebook_id] = name
103 103 self.rev_mapping[name] = notebook_id
104 104 return notebook_id
105 105
106 106 def delete_notebook_id(self, notebook_id):
107 107 """Delete a notebook's id only. This doesn't delete the actual notebook."""
108 108 name = self.mapping[notebook_id]
109 109 del self.mapping[notebook_id]
110 110 del self.rev_mapping[name]
111 111
112 112 def notebook_exists(self, notebook_id):
113 113 """Does a notebook exist?"""
114 114 if notebook_id not in self.mapping:
115 115 return False
116 116 path = self.get_path_by_name(self.mapping[notebook_id])
117 117 return os.path.isfile(path)
118 118
119 119 def find_path(self, notebook_id):
120 120 """Return a full path to a notebook given its notebook_id."""
121 121 try:
122 122 name = self.mapping[notebook_id]
123 123 except KeyError:
124 124 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
125 125 return self.get_path_by_name(name)
126 126
127 127 def get_path_by_name(self, name):
128 128 """Return a full path to a notebook given its name."""
129 129 filename = name + self.filename_ext
130 130 path = os.path.join(self.notebook_dir, filename)
131 131 return path
132 132
133 133 def get_notebook(self, notebook_id, format=u'json'):
134 134 """Get the representation of a notebook in format by notebook_id."""
135 135 format = unicode(format)
136 136 if format not in self.allowed_formats:
137 137 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
138 138 last_modified, nb = self.get_notebook_object(notebook_id)
139 139 kwargs = {}
140 140 if format == 'json':
141 141 # don't split lines for sending over the wire, because it
142 142 # should match the Python in-memory format.
143 143 kwargs['split_lines'] = False
144 144 data = current.writes(nb, format, **kwargs)
145 145 name = nb.get('name','notebook')
146 146 return last_modified, name, data
147 147
148 148 def get_notebook_object(self, notebook_id):
149 149 """Get the NotebookNode representation of a notebook by notebook_id."""
150 150 path = self.find_path(notebook_id)
151 151 if not os.path.isfile(path):
152 152 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
153 153 info = os.stat(path)
154 154 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
155 155 with open(path,'r') as f:
156 156 s = f.read()
157 157 try:
158 158 # v1 and v2 and json in the .ipynb files.
159 159 nb = current.reads(s, u'json')
160 160 except:
161 161 raise web.HTTPError(500, u'Unreadable JSON notebook.')
162 162 if 'name' not in nb:
163 163 nb.name = os.path.split(path)[-1].split(u'.')[0]
164 164 return last_modified, nb
165 165
166 166 def save_new_notebook(self, data, name=None, format=u'json'):
167 167 """Save a new notebook and return its notebook_id.
168 168
169 169 If a name is passed in, it overrides any values in the notebook data
170 170 and the value in the data is updated to use that value.
171 171 """
172 172 if format not in self.allowed_formats:
173 173 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
174 174
175 175 try:
176 176 nb = current.reads(data.decode('utf-8'), format)
177 177 except:
178 178 raise web.HTTPError(400, u'Invalid JSON data')
179 179
180 180 if name is None:
181 181 try:
182 182 name = nb.metadata.name
183 183 except AttributeError:
184 184 raise web.HTTPError(400, u'Missing notebook name')
185 185 nb.metadata.name = name
186 186
187 187 notebook_id = self.new_notebook_id(name)
188 188 self.save_notebook_object(notebook_id, nb)
189 189 return notebook_id
190 190
191 191 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
192 192 """Save an existing notebook by notebook_id."""
193 193 if format not in self.allowed_formats:
194 194 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
195 195
196 196 try:
197 197 nb = current.reads(data.decode('utf-8'), format)
198 198 except:
199 199 raise web.HTTPError(400, u'Invalid JSON data')
200 200
201 201 if name is not None:
202 202 nb.metadata.name = name
203 203 self.save_notebook_object(notebook_id, nb)
204 204
205 205 def save_notebook_object(self, notebook_id, nb):
206 206 """Save an existing notebook object by notebook_id."""
207 207 if notebook_id not in self.mapping:
208 208 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
209 209 old_name = self.mapping[notebook_id]
210 210 try:
211 211 new_name = nb.metadata.name
212 212 except AttributeError:
213 213 raise web.HTTPError(400, u'Missing notebook name')
214 214 path = self.get_path_by_name(new_name)
215 215 try:
216 216 with open(path,'w') as f:
217 217 current.write(nb, f, u'json')
218 218 except Exception as e:
219 219 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
220 220 # save .py script as well
221 221 if self.save_script:
222 222 pypath = os.path.splitext(path)[0] + '.py'
223 223 try:
224 224 with open(pypath,'w') as f:
225 225 current.write(nb, f, u'py')
226 226 except Exception as e:
227 227 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
228 228
229 229 if old_name != new_name:
230 230 old_path = self.get_path_by_name(old_name)
231 231 if os.path.isfile(old_path):
232 232 os.unlink(old_path)
233 233 if self.save_script:
234 234 old_pypath = os.path.splitext(old_path)[0] + '.py'
235 235 if os.path.isfile(old_pypath):
236 236 os.unlink(old_pypath)
237 237 self.mapping[notebook_id] = new_name
238 238 self.rev_mapping[new_name] = notebook_id
239 239
240 240 def delete_notebook(self, notebook_id):
241 241 """Delete notebook by notebook_id."""
242 242 path = self.find_path(notebook_id)
243 243 if not os.path.isfile(path):
244 244 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
245 245 os.unlink(path)
246 246 self.delete_notebook_id(notebook_id)
247 247
248 248 def new_notebook(self):
249 249 """Create a new notebook and returns its notebook_id."""
250 250 i = 0
251 251 while True:
252 252 name = u'Untitled%i' % i
253 253 path = self.get_path_by_name(name)
254 254 if not os.path.isfile(path):
255 255 break
256 256 else:
257 257 i = i+1
258 258 notebook_id = self.new_notebook_id(name)
259 259 metadata = current.new_metadata(name=name)
260 260 nb = current.new_notebook(metadata=metadata)
261 261 with open(path,'w') as f:
262 262 current.write(nb, f, u'json')
263 263 return notebook_id
264 264
General Comments 0
You need to be logged in to leave comments. Login now