##// END OF EJS Templates
Server side logic for directories.
Brian E. Granger -
Show More
@@ -1,418 +1,451 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 * Zach Sailer
6 * Zach Sailer
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2011 The IPython Development Team
10 # Copyright (C) 2011 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 import io
20 import io
21 import itertools
21 import itertools
22 import os
22 import os
23 import glob
23 import glob
24 import shutil
24 import shutil
25
25
26 from tornado import web
26 from tornado import web
27
27
28 from .nbmanager import NotebookManager
28 from .nbmanager import NotebookManager
29 from IPython.nbformat import current
29 from IPython.nbformat import current
30 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
30 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
31 from IPython.utils import tz
31 from IPython.utils import tz
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Classes
34 # Classes
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 class FileNotebookManager(NotebookManager):
37 class FileNotebookManager(NotebookManager):
38
38
39 save_script = Bool(False, config=True,
39 save_script = Bool(False, config=True,
40 help="""Automatically create a Python script when saving the notebook.
40 help="""Automatically create a Python script when saving the notebook.
41
41
42 For easier use of import, %run and %load across notebooks, a
42 For easier use of import, %run and %load across notebooks, a
43 <notebook-name>.py script will be created next to any
43 <notebook-name>.py script will be created next to any
44 <notebook-name>.ipynb on each save. This can also be set with the
44 <notebook-name>.ipynb on each save. This can also be set with the
45 short `--script` flag.
45 short `--script` flag.
46 """
46 """
47 )
47 )
48
48
49 checkpoint_dir = Unicode(config=True,
49 checkpoint_dir = Unicode(config=True,
50 help="""The location in which to keep notebook checkpoints
50 help="""The location in which to keep notebook checkpoints
51
51
52 By default, it is notebook-dir/.ipynb_checkpoints
52 By default, it is notebook-dir/.ipynb_checkpoints
53 """
53 """
54 )
54 )
55 def _checkpoint_dir_default(self):
55 def _checkpoint_dir_default(self):
56 return os.path.join(self.notebook_dir, '.ipynb_checkpoints')
56 return os.path.join(self.notebook_dir, '.ipynb_checkpoints')
57
57
58 def _checkpoint_dir_changed(self, name, old, new):
58 def _checkpoint_dir_changed(self, name, old, new):
59 """do a bit of validation of the checkpoint dir"""
59 """do a bit of validation of the checkpoint dir"""
60 if not os.path.isabs(new):
60 if not os.path.isabs(new):
61 # If we receive a non-absolute path, make it absolute.
61 # If we receive a non-absolute path, make it absolute.
62 abs_new = os.path.abspath(new)
62 abs_new = os.path.abspath(new)
63 self.checkpoint_dir = abs_new
63 self.checkpoint_dir = abs_new
64 return
64 return
65 if os.path.exists(new) and not os.path.isdir(new):
65 if os.path.exists(new) and not os.path.isdir(new):
66 raise TraitError("checkpoint dir %r is not a directory" % new)
66 raise TraitError("checkpoint dir %r is not a directory" % new)
67 if not os.path.exists(new):
67 if not os.path.exists(new):
68 self.log.info("Creating checkpoint dir %s", new)
68 self.log.info("Creating checkpoint dir %s", new)
69 try:
69 try:
70 os.mkdir(new)
70 os.mkdir(new)
71 except:
71 except:
72 raise TraitError("Couldn't create checkpoint dir %r" % new)
72 raise TraitError("Couldn't create checkpoint dir %r" % new)
73
73
74 def get_notebook_names(self, path=''):
74 def get_notebook_names(self, path=''):
75 """List all notebook names in the notebook dir and path."""
75 """List all notebook names in the notebook dir and path."""
76 path = path.strip('/')
76 path = path.strip('/')
77 if not os.path.isdir(self.get_os_path(path=path)):
77 if not os.path.isdir(self.get_os_path(path=path)):
78 raise web.HTTPError(404, 'Directory not found: ' + path)
78 raise web.HTTPError(404, 'Directory not found: ' + path)
79 names = glob.glob(self.get_os_path('*'+self.filename_ext, path))
79 names = glob.glob(self.get_os_path('*'+self.filename_ext, path))
80 names = [os.path.basename(name)
80 names = [os.path.basename(name)
81 for name in names]
81 for name in names]
82 return names
82 return names
83
83
84 def increment_filename(self, basename, path='', ext='.ipynb'):
84 def increment_filename(self, basename, path='', ext='.ipynb'):
85 """Return a non-used filename of the form basename<int>."""
85 """Return a non-used filename of the form basename<int>."""
86 path = path.strip('/')
86 path = path.strip('/')
87 for i in itertools.count():
87 for i in itertools.count():
88 name = u'{basename}{i}{ext}'.format(basename=basename, i=i, ext=ext)
88 name = u'{basename}{i}{ext}'.format(basename=basename, i=i, ext=ext)
89 os_path = self.get_os_path(name, path)
89 os_path = self.get_os_path(name, path)
90 if not os.path.isfile(os_path):
90 if not os.path.isfile(os_path):
91 break
91 break
92 return name
92 return name
93
93
94 def path_exists(self, path):
94 def path_exists(self, path):
95 """Does the API-style path (directory) actually exist?
95 """Does the API-style path (directory) actually exist?
96
96
97 Parameters
97 Parameters
98 ----------
98 ----------
99 path : string
99 path : string
100 The path to check. This is an API path (`/` separated,
100 The path to check. This is an API path (`/` separated,
101 relative to base notebook-dir).
101 relative to base notebook-dir).
102
102
103 Returns
103 Returns
104 -------
104 -------
105 exists : bool
105 exists : bool
106 Whether the path is indeed a directory.
106 Whether the path is indeed a directory.
107 """
107 """
108 path = path.strip('/')
108 path = path.strip('/')
109 os_path = self.get_os_path(path=path)
109 os_path = self.get_os_path(path=path)
110 return os.path.isdir(os_path)
110 return os.path.isdir(os_path)
111
111
112 def get_os_path(self, name=None, path=''):
112 def get_os_path(self, name=None, path=''):
113 """Given a notebook name and a URL path, return its file system
113 """Given a notebook name and a URL path, return its file system
114 path.
114 path.
115
115
116 Parameters
116 Parameters
117 ----------
117 ----------
118 name : string
118 name : string
119 The name of a notebook file with the .ipynb extension
119 The name of a notebook file with the .ipynb extension
120 path : string
120 path : string
121 The relative URL path (with '/' as separator) to the named
121 The relative URL path (with '/' as separator) to the named
122 notebook.
122 notebook.
123
123
124 Returns
124 Returns
125 -------
125 -------
126 path : string
126 path : string
127 A file system path that combines notebook_dir (location where
127 A file system path that combines notebook_dir (location where
128 server started), the relative path, and the filename with the
128 server started), the relative path, and the filename with the
129 current operating system's url.
129 current operating system's url.
130 """
130 """
131 parts = path.strip('/').split('/')
131 parts = path.strip('/').split('/')
132 parts = [p for p in parts if p != ''] # remove duplicate splits
132 parts = [p for p in parts if p != ''] # remove duplicate splits
133 if name is not None:
133 if name is not None:
134 parts.append(name)
134 parts.append(name)
135 path = os.path.join(self.notebook_dir, *parts)
135 path = os.path.join(self.notebook_dir, *parts)
136 return path
136 return path
137
137
138 def notebook_exists(self, name, path=''):
138 def notebook_exists(self, name, path=''):
139 """Returns a True if the notebook exists. Else, returns False.
139 """Returns a True if the notebook exists. Else, returns False.
140
140
141 Parameters
141 Parameters
142 ----------
142 ----------
143 name : string
143 name : string
144 The name of the notebook you are checking.
144 The name of the notebook you are checking.
145 path : string
145 path : string
146 The relative path to the notebook (with '/' as separator)
146 The relative path to the notebook (with '/' as separator)
147
147
148 Returns
148 Returns
149 -------
149 -------
150 bool
150 bool
151 """
151 """
152 path = path.strip('/')
152 path = path.strip('/')
153 nbpath = self.get_os_path(name, path=path)
153 nbpath = self.get_os_path(name, path=path)
154 return os.path.isfile(nbpath)
154 return os.path.isfile(nbpath)
155
155
156 def list_dirs(self, path):
157 """List the directories for a given API style path."""
158 path = path.strip('/')
159 os_path = self.get_os_path('', path)
160 dir_names = os.listdir(os_path)
161 dirs = []
162 for name in dir_names:
163 os_path = self.get_os_path(name, path)
164 if os.path.isdir(os_path) and not name.startswith('.'):
165 model = self.get_dir_model(name, path)
166 dirs.append(model)
167 dirs = sorted(dirs, key=lambda item: item['name'])
168 return dirs
169
170 def get_dir_model(self, name, path=''):
171 """Get the directory model given a directory name and its API style path"""
172 path = path.strip('/')
173 os_path = self.get_os_path(name, path)
174 if not os.path.isdir(os_path):
175 raise IOError('directory does not exist: %r' % os_path)
176 info = os.stat(os_path)
177 last_modified = tz.utcfromtimestamp(info.st_mtime)
178 created = tz.utcfromtimestamp(info.st_ctime)
179 # Create the notebook model.
180 model ={}
181 model['name'] = name
182 model['path'] = path
183 model['last_modified'] = last_modified
184 model['created'] = created
185 model['type'] = 'directory'
186 return model
187
156 def list_notebooks(self, path):
188 def list_notebooks(self, path):
157 """Returns a list of dictionaries that are the standard model
189 """Returns a list of dictionaries that are the standard model
158 for all notebooks in the relative 'path'.
190 for all notebooks in the relative 'path'.
159
191
160 Parameters
192 Parameters
161 ----------
193 ----------
162 path : str
194 path : str
163 the URL path that describes the relative path for the
195 the URL path that describes the relative path for the
164 listed notebooks
196 listed notebooks
165
197
166 Returns
198 Returns
167 -------
199 -------
168 notebooks : list of dicts
200 notebooks : list of dicts
169 a list of the notebook models without 'content'
201 a list of the notebook models without 'content'
170 """
202 """
171 path = path.strip('/')
203 path = path.strip('/')
172 notebook_names = self.get_notebook_names(path)
204 notebook_names = self.get_notebook_names(path)
173 notebooks = []
205 notebooks = []
174 for name in notebook_names:
206 for name in notebook_names:
175 model = self.get_notebook_model(name, path, content=False)
207 model = self.get_notebook_model(name, path, content=False)
176 notebooks.append(model)
208 notebooks.append(model)
177 notebooks = sorted(notebooks, key=lambda item: item['name'])
209 notebooks = sorted(notebooks, key=lambda item: item['name'])
210 notebooks = self.list_dirs(path) + notebooks
178 return notebooks
211 return notebooks
179
212
180 def get_notebook_model(self, name, path='', content=True):
213 def get_notebook_model(self, name, path='', content=True):
181 """ Takes a path and name for a notebook and returns its model
214 """ Takes a path and name for a notebook and returns its model
182
215
183 Parameters
216 Parameters
184 ----------
217 ----------
185 name : str
218 name : str
186 the name of the notebook
219 the name of the notebook
187 path : str
220 path : str
188 the URL path that describes the relative path for
221 the URL path that describes the relative path for
189 the notebook
222 the notebook
190
223
191 Returns
224 Returns
192 -------
225 -------
193 model : dict
226 model : dict
194 the notebook model. If contents=True, returns the 'contents'
227 the notebook model. If contents=True, returns the 'contents'
195 dict in the model as well.
228 dict in the model as well.
196 """
229 """
197 path = path.strip('/')
230 path = path.strip('/')
198 if not self.notebook_exists(name=name, path=path):
231 if not self.notebook_exists(name=name, path=path):
199 raise web.HTTPError(404, u'Notebook does not exist: %s' % name)
232 raise web.HTTPError(404, u'Notebook does not exist: %s' % name)
200 os_path = self.get_os_path(name, path)
233 os_path = self.get_os_path(name, path)
201 info = os.stat(os_path)
234 info = os.stat(os_path)
202 last_modified = tz.utcfromtimestamp(info.st_mtime)
235 last_modified = tz.utcfromtimestamp(info.st_mtime)
203 created = tz.utcfromtimestamp(info.st_ctime)
236 created = tz.utcfromtimestamp(info.st_ctime)
204 # Create the notebook model.
237 # Create the notebook model.
205 model ={}
238 model ={}
206 model['name'] = name
239 model['name'] = name
207 model['path'] = path
240 model['path'] = path
208 model['last_modified'] = last_modified
241 model['last_modified'] = last_modified
209 model['created'] = created
242 model['created'] = created
210 if content:
243 if content:
211 with io.open(os_path, 'r', encoding='utf-8') as f:
244 with io.open(os_path, 'r', encoding='utf-8') as f:
212 try:
245 try:
213 nb = current.read(f, u'json')
246 nb = current.read(f, u'json')
214 except Exception as e:
247 except Exception as e:
215 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
248 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
216 self.mark_trusted_cells(nb, path, name)
249 self.mark_trusted_cells(nb, path, name)
217 model['content'] = nb
250 model['content'] = nb
218 return model
251 return model
219
252
220 def save_notebook_model(self, model, name='', path=''):
253 def save_notebook_model(self, model, name='', path=''):
221 """Save the notebook model and return the model with no content."""
254 """Save the notebook model and return the model with no content."""
222 path = path.strip('/')
255 path = path.strip('/')
223
256
224 if 'content' not in model:
257 if 'content' not in model:
225 raise web.HTTPError(400, u'No notebook JSON data provided')
258 raise web.HTTPError(400, u'No notebook JSON data provided')
226
259
227 # One checkpoint should always exist
260 # One checkpoint should always exist
228 if self.notebook_exists(name, path) and not self.list_checkpoints(name, path):
261 if self.notebook_exists(name, path) and not self.list_checkpoints(name, path):
229 self.create_checkpoint(name, path)
262 self.create_checkpoint(name, path)
230
263
231 new_path = model.get('path', path).strip('/')
264 new_path = model.get('path', path).strip('/')
232 new_name = model.get('name', name)
265 new_name = model.get('name', name)
233
266
234 if path != new_path or name != new_name:
267 if path != new_path or name != new_name:
235 self.rename_notebook(name, path, new_name, new_path)
268 self.rename_notebook(name, path, new_name, new_path)
236
269
237 # Save the notebook file
270 # Save the notebook file
238 os_path = self.get_os_path(new_name, new_path)
271 os_path = self.get_os_path(new_name, new_path)
239 nb = current.to_notebook_json(model['content'])
272 nb = current.to_notebook_json(model['content'])
240
273
241 self.check_and_sign(nb, new_path, new_name)
274 self.check_and_sign(nb, new_path, new_name)
242
275
243 if 'name' in nb['metadata']:
276 if 'name' in nb['metadata']:
244 nb['metadata']['name'] = u''
277 nb['metadata']['name'] = u''
245 try:
278 try:
246 self.log.debug("Autosaving notebook %s", os_path)
279 self.log.debug("Autosaving notebook %s", os_path)
247 with io.open(os_path, 'w', encoding='utf-8') as f:
280 with io.open(os_path, 'w', encoding='utf-8') as f:
248 current.write(nb, f, u'json')
281 current.write(nb, f, u'json')
249 except Exception as e:
282 except Exception as e:
250 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s %s' % (os_path, e))
283 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s %s' % (os_path, e))
251
284
252 # Save .py script as well
285 # Save .py script as well
253 if self.save_script:
286 if self.save_script:
254 py_path = os.path.splitext(os_path)[0] + '.py'
287 py_path = os.path.splitext(os_path)[0] + '.py'
255 self.log.debug("Writing script %s", py_path)
288 self.log.debug("Writing script %s", py_path)
256 try:
289 try:
257 with io.open(py_path, 'w', encoding='utf-8') as f:
290 with io.open(py_path, 'w', encoding='utf-8') as f:
258 current.write(nb, f, u'py')
291 current.write(nb, f, u'py')
259 except Exception as e:
292 except Exception as e:
260 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s %s' % (py_path, e))
293 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s %s' % (py_path, e))
261
294
262 model = self.get_notebook_model(new_name, new_path, content=False)
295 model = self.get_notebook_model(new_name, new_path, content=False)
263 return model
296 return model
264
297
265 def update_notebook_model(self, model, name, path=''):
298 def update_notebook_model(self, model, name, path=''):
266 """Update the notebook's path and/or name"""
299 """Update the notebook's path and/or name"""
267 path = path.strip('/')
300 path = path.strip('/')
268 new_name = model.get('name', name)
301 new_name = model.get('name', name)
269 new_path = model.get('path', path).strip('/')
302 new_path = model.get('path', path).strip('/')
270 if path != new_path or name != new_name:
303 if path != new_path or name != new_name:
271 self.rename_notebook(name, path, new_name, new_path)
304 self.rename_notebook(name, path, new_name, new_path)
272 model = self.get_notebook_model(new_name, new_path, content=False)
305 model = self.get_notebook_model(new_name, new_path, content=False)
273 return model
306 return model
274
307
275 def delete_notebook_model(self, name, path=''):
308 def delete_notebook_model(self, name, path=''):
276 """Delete notebook by name and path."""
309 """Delete notebook by name and path."""
277 path = path.strip('/')
310 path = path.strip('/')
278 os_path = self.get_os_path(name, path)
311 os_path = self.get_os_path(name, path)
279 if not os.path.isfile(os_path):
312 if not os.path.isfile(os_path):
280 raise web.HTTPError(404, u'Notebook does not exist: %s' % os_path)
313 raise web.HTTPError(404, u'Notebook does not exist: %s' % os_path)
281
314
282 # clear checkpoints
315 # clear checkpoints
283 for checkpoint in self.list_checkpoints(name, path):
316 for checkpoint in self.list_checkpoints(name, path):
284 checkpoint_id = checkpoint['id']
317 checkpoint_id = checkpoint['id']
285 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
318 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
286 if os.path.isfile(cp_path):
319 if os.path.isfile(cp_path):
287 self.log.debug("Unlinking checkpoint %s", cp_path)
320 self.log.debug("Unlinking checkpoint %s", cp_path)
288 os.unlink(cp_path)
321 os.unlink(cp_path)
289
322
290 self.log.debug("Unlinking notebook %s", os_path)
323 self.log.debug("Unlinking notebook %s", os_path)
291 os.unlink(os_path)
324 os.unlink(os_path)
292
325
293 def rename_notebook(self, old_name, old_path, new_name, new_path):
326 def rename_notebook(self, old_name, old_path, new_name, new_path):
294 """Rename a notebook."""
327 """Rename a notebook."""
295 old_path = old_path.strip('/')
328 old_path = old_path.strip('/')
296 new_path = new_path.strip('/')
329 new_path = new_path.strip('/')
297 if new_name == old_name and new_path == old_path:
330 if new_name == old_name and new_path == old_path:
298 return
331 return
299
332
300 new_os_path = self.get_os_path(new_name, new_path)
333 new_os_path = self.get_os_path(new_name, new_path)
301 old_os_path = self.get_os_path(old_name, old_path)
334 old_os_path = self.get_os_path(old_name, old_path)
302
335
303 # Should we proceed with the move?
336 # Should we proceed with the move?
304 if os.path.isfile(new_os_path):
337 if os.path.isfile(new_os_path):
305 raise web.HTTPError(409, u'Notebook with name already exists: %s' % new_os_path)
338 raise web.HTTPError(409, u'Notebook with name already exists: %s' % new_os_path)
306 if self.save_script:
339 if self.save_script:
307 old_py_path = os.path.splitext(old_os_path)[0] + '.py'
340 old_py_path = os.path.splitext(old_os_path)[0] + '.py'
308 new_py_path = os.path.splitext(new_os_path)[0] + '.py'
341 new_py_path = os.path.splitext(new_os_path)[0] + '.py'
309 if os.path.isfile(new_py_path):
342 if os.path.isfile(new_py_path):
310 raise web.HTTPError(409, u'Python script with name already exists: %s' % new_py_path)
343 raise web.HTTPError(409, u'Python script with name already exists: %s' % new_py_path)
311
344
312 # Move the notebook file
345 # Move the notebook file
313 try:
346 try:
314 os.rename(old_os_path, new_os_path)
347 os.rename(old_os_path, new_os_path)
315 except Exception as e:
348 except Exception as e:
316 raise web.HTTPError(500, u'Unknown error renaming notebook: %s %s' % (old_os_path, e))
349 raise web.HTTPError(500, u'Unknown error renaming notebook: %s %s' % (old_os_path, e))
317
350
318 # Move the checkpoints
351 # Move the checkpoints
319 old_checkpoints = self.list_checkpoints(old_name, old_path)
352 old_checkpoints = self.list_checkpoints(old_name, old_path)
320 for cp in old_checkpoints:
353 for cp in old_checkpoints:
321 checkpoint_id = cp['id']
354 checkpoint_id = cp['id']
322 old_cp_path = self.get_checkpoint_path(checkpoint_id, old_name, old_path)
355 old_cp_path = self.get_checkpoint_path(checkpoint_id, old_name, old_path)
323 new_cp_path = self.get_checkpoint_path(checkpoint_id, new_name, new_path)
356 new_cp_path = self.get_checkpoint_path(checkpoint_id, new_name, new_path)
324 if os.path.isfile(old_cp_path):
357 if os.path.isfile(old_cp_path):
325 self.log.debug("Renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
358 self.log.debug("Renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
326 os.rename(old_cp_path, new_cp_path)
359 os.rename(old_cp_path, new_cp_path)
327
360
328 # Move the .py script
361 # Move the .py script
329 if self.save_script:
362 if self.save_script:
330 os.rename(old_py_path, new_py_path)
363 os.rename(old_py_path, new_py_path)
331
364
332 # Checkpoint-related utilities
365 # Checkpoint-related utilities
333
366
334 def get_checkpoint_path(self, checkpoint_id, name, path=''):
367 def get_checkpoint_path(self, checkpoint_id, name, path=''):
335 """find the path to a checkpoint"""
368 """find the path to a checkpoint"""
336 path = path.strip('/')
369 path = path.strip('/')
337 basename, _ = os.path.splitext(name)
370 basename, _ = os.path.splitext(name)
338 filename = u"{name}-{checkpoint_id}{ext}".format(
371 filename = u"{name}-{checkpoint_id}{ext}".format(
339 name=basename,
372 name=basename,
340 checkpoint_id=checkpoint_id,
373 checkpoint_id=checkpoint_id,
341 ext=self.filename_ext,
374 ext=self.filename_ext,
342 )
375 )
343 cp_path = os.path.join(path, self.checkpoint_dir, filename)
376 cp_path = os.path.join(path, self.checkpoint_dir, filename)
344 return cp_path
377 return cp_path
345
378
346 def get_checkpoint_model(self, checkpoint_id, name, path=''):
379 def get_checkpoint_model(self, checkpoint_id, name, path=''):
347 """construct the info dict for a given checkpoint"""
380 """construct the info dict for a given checkpoint"""
348 path = path.strip('/')
381 path = path.strip('/')
349 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
382 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
350 stats = os.stat(cp_path)
383 stats = os.stat(cp_path)
351 last_modified = tz.utcfromtimestamp(stats.st_mtime)
384 last_modified = tz.utcfromtimestamp(stats.st_mtime)
352 info = dict(
385 info = dict(
353 id = checkpoint_id,
386 id = checkpoint_id,
354 last_modified = last_modified,
387 last_modified = last_modified,
355 )
388 )
356 return info
389 return info
357
390
358 # public checkpoint API
391 # public checkpoint API
359
392
360 def create_checkpoint(self, name, path=''):
393 def create_checkpoint(self, name, path=''):
361 """Create a checkpoint from the current state of a notebook"""
394 """Create a checkpoint from the current state of a notebook"""
362 path = path.strip('/')
395 path = path.strip('/')
363 nb_path = self.get_os_path(name, path)
396 nb_path = self.get_os_path(name, path)
364 # only the one checkpoint ID:
397 # only the one checkpoint ID:
365 checkpoint_id = u"checkpoint"
398 checkpoint_id = u"checkpoint"
366 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
399 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
367 self.log.debug("creating checkpoint for notebook %s", name)
400 self.log.debug("creating checkpoint for notebook %s", name)
368 if not os.path.exists(self.checkpoint_dir):
401 if not os.path.exists(self.checkpoint_dir):
369 os.mkdir(self.checkpoint_dir)
402 os.mkdir(self.checkpoint_dir)
370 shutil.copy2(nb_path, cp_path)
403 shutil.copy2(nb_path, cp_path)
371
404
372 # return the checkpoint info
405 # return the checkpoint info
373 return self.get_checkpoint_model(checkpoint_id, name, path)
406 return self.get_checkpoint_model(checkpoint_id, name, path)
374
407
375 def list_checkpoints(self, name, path=''):
408 def list_checkpoints(self, name, path=''):
376 """list the checkpoints for a given notebook
409 """list the checkpoints for a given notebook
377
410
378 This notebook manager currently only supports one checkpoint per notebook.
411 This notebook manager currently only supports one checkpoint per notebook.
379 """
412 """
380 path = path.strip('/')
413 path = path.strip('/')
381 checkpoint_id = "checkpoint"
414 checkpoint_id = "checkpoint"
382 path = self.get_checkpoint_path(checkpoint_id, name, path)
415 path = self.get_checkpoint_path(checkpoint_id, name, path)
383 if not os.path.exists(path):
416 if not os.path.exists(path):
384 return []
417 return []
385 else:
418 else:
386 return [self.get_checkpoint_model(checkpoint_id, name, path)]
419 return [self.get_checkpoint_model(checkpoint_id, name, path)]
387
420
388
421
389 def restore_checkpoint(self, checkpoint_id, name, path=''):
422 def restore_checkpoint(self, checkpoint_id, name, path=''):
390 """restore a notebook to a checkpointed state"""
423 """restore a notebook to a checkpointed state"""
391 path = path.strip('/')
424 path = path.strip('/')
392 self.log.info("restoring Notebook %s from checkpoint %s", name, checkpoint_id)
425 self.log.info("restoring Notebook %s from checkpoint %s", name, checkpoint_id)
393 nb_path = self.get_os_path(name, path)
426 nb_path = self.get_os_path(name, path)
394 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
427 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
395 if not os.path.isfile(cp_path):
428 if not os.path.isfile(cp_path):
396 self.log.debug("checkpoint file does not exist: %s", cp_path)
429 self.log.debug("checkpoint file does not exist: %s", cp_path)
397 raise web.HTTPError(404,
430 raise web.HTTPError(404,
398 u'Notebook checkpoint does not exist: %s-%s' % (name, checkpoint_id)
431 u'Notebook checkpoint does not exist: %s-%s' % (name, checkpoint_id)
399 )
432 )
400 # ensure notebook is readable (never restore from an unreadable notebook)
433 # ensure notebook is readable (never restore from an unreadable notebook)
401 with io.open(cp_path, 'r', encoding='utf-8') as f:
434 with io.open(cp_path, 'r', encoding='utf-8') as f:
402 nb = current.read(f, u'json')
435 nb = current.read(f, u'json')
403 shutil.copy2(cp_path, nb_path)
436 shutil.copy2(cp_path, nb_path)
404 self.log.debug("copying %s -> %s", cp_path, nb_path)
437 self.log.debug("copying %s -> %s", cp_path, nb_path)
405
438
406 def delete_checkpoint(self, checkpoint_id, name, path=''):
439 def delete_checkpoint(self, checkpoint_id, name, path=''):
407 """delete a notebook's checkpoint"""
440 """delete a notebook's checkpoint"""
408 path = path.strip('/')
441 path = path.strip('/')
409 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
442 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
410 if not os.path.isfile(cp_path):
443 if not os.path.isfile(cp_path):
411 raise web.HTTPError(404,
444 raise web.HTTPError(404,
412 u'Notebook checkpoint does not exist: %s%s-%s' % (path, name, checkpoint_id)
445 u'Notebook checkpoint does not exist: %s%s-%s' % (path, name, checkpoint_id)
413 )
446 )
414 self.log.debug("unlinking %s", cp_path)
447 self.log.debug("unlinking %s", cp_path)
415 os.unlink(cp_path)
448 os.unlink(cp_path)
416
449
417 def info_string(self):
450 def info_string(self):
418 return "Serving notebooks from local directory: %s" % self.notebook_dir
451 return "Serving notebooks from local directory: %s" % self.notebook_dir
@@ -1,403 +1,404 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // NotebookList
9 // NotebookList
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13 "use strict";
13 "use strict";
14
14
15 var utils = IPython.utils;
15 var utils = IPython.utils;
16
16
17 var NotebookList = function (selector) {
17 var NotebookList = function (selector) {
18 this.selector = selector;
18 this.selector = selector;
19 if (this.selector !== undefined) {
19 if (this.selector !== undefined) {
20 this.element = $(selector);
20 this.element = $(selector);
21 this.style();
21 this.style();
22 this.bind_events();
22 this.bind_events();
23 }
23 }
24 this.notebooks_list = [];
24 this.notebooks_list = [];
25 this.sessions = {};
25 this.sessions = {};
26 };
26 };
27
27
28 NotebookList.prototype.baseProjectUrl = function () {
28 NotebookList.prototype.baseProjectUrl = function () {
29 return $('body').data('baseProjectUrl');
29 return $('body').data('baseProjectUrl');
30 };
30 };
31
31
32 NotebookList.prototype.notebookPath = function() {
32 NotebookList.prototype.notebookPath = function() {
33 return $('body').data('notebookPath');
33 return $('body').data('notebookPath');
34 };
34 };
35
35
36 NotebookList.prototype.style = function () {
36 NotebookList.prototype.style = function () {
37 $('#notebook_toolbar').addClass('list_toolbar');
37 $('#notebook_toolbar').addClass('list_toolbar');
38 $('#drag_info').addClass('toolbar_info');
38 $('#drag_info').addClass('toolbar_info');
39 $('#notebook_buttons').addClass('toolbar_buttons');
39 $('#notebook_buttons').addClass('toolbar_buttons');
40 $('#notebook_list_header').addClass('list_header');
40 $('#notebook_list_header').addClass('list_header');
41 this.element.addClass("list_container");
41 this.element.addClass("list_container");
42 };
42 };
43
43
44
44
45 NotebookList.prototype.bind_events = function () {
45 NotebookList.prototype.bind_events = function () {
46 var that = this;
46 var that = this;
47 $('#refresh_notebook_list').click(function () {
47 $('#refresh_notebook_list').click(function () {
48 that.load_list();
48 that.load_list();
49 });
49 });
50 this.element.bind('dragover', function () {
50 this.element.bind('dragover', function () {
51 return false;
51 return false;
52 });
52 });
53 this.element.bind('drop', function(event){
53 this.element.bind('drop', function(event){
54 that.handelFilesUpload(event,'drop');
54 that.handelFilesUpload(event,'drop');
55 return false;
55 return false;
56 });
56 });
57 };
57 };
58
58
59 NotebookList.prototype.handelFilesUpload = function(event, dropOrForm) {
59 NotebookList.prototype.handelFilesUpload = function(event, dropOrForm) {
60 var that = this;
60 var that = this;
61 var files;
61 var files;
62 if(dropOrForm =='drop'){
62 if(dropOrForm =='drop'){
63 files = event.originalEvent.dataTransfer.files;
63 files = event.originalEvent.dataTransfer.files;
64 } else
64 } else
65 {
65 {
66 files = event.originalEvent.target.files;
66 files = event.originalEvent.target.files;
67 }
67 }
68 for (var i = 0; i < files.length; i++) {
68 for (var i = 0; i < files.length; i++) {
69 var f = files[i];
69 var f = files[i];
70 var reader = new FileReader();
70 var reader = new FileReader();
71 reader.readAsText(f);
71 reader.readAsText(f);
72 var name_and_ext = utils.splitext(f.name);
72 var name_and_ext = utils.splitext(f.name);
73 var nbname = name_and_ext[0];
73 var nbname = name_and_ext[0];
74 var file_ext = name_and_ext[1];
74 var file_ext = name_and_ext[1];
75 if (file_ext === '.ipynb') {
75 if (file_ext === '.ipynb') {
76 var item = that.new_notebook_item(0);
76 var item = that.new_notebook_item(0);
77 that.add_name_input(nbname, item);
77 that.add_name_input(nbname, item);
78 // Store the notebook item in the reader so we can use it later
78 // Store the notebook item in the reader so we can use it later
79 // to know which item it belongs to.
79 // to know which item it belongs to.
80 $(reader).data('item', item);
80 $(reader).data('item', item);
81 reader.onload = function (event) {
81 reader.onload = function (event) {
82 var nbitem = $(event.target).data('item');
82 var nbitem = $(event.target).data('item');
83 that.add_notebook_data(event.target.result, nbitem);
83 that.add_notebook_data(event.target.result, nbitem);
84 that.add_upload_button(nbitem);
84 that.add_upload_button(nbitem);
85 };
85 };
86 } else {
86 } else {
87 var dialog = 'Uploaded notebooks must be .ipynb files';
87 var dialog = 'Uploaded notebooks must be .ipynb files';
88 IPython.dialog.modal({
88 IPython.dialog.modal({
89 title : 'Invalid file type',
89 title : 'Invalid file type',
90 body : dialog,
90 body : dialog,
91 buttons : {'OK' : {'class' : 'btn-primary'}}
91 buttons : {'OK' : {'class' : 'btn-primary'}}
92 });
92 });
93 }
93 }
94 }
94 }
95 // Replace the file input form wth a clone of itself. This is required to
95 // Replace the file input form wth a clone of itself. This is required to
96 // reset the form. Otherwise, if you upload a file, delete it and try to
96 // reset the form. Otherwise, if you upload a file, delete it and try to
97 // upload it again, the changed event won't fire.
97 // upload it again, the changed event won't fire.
98 var form = $('input.fileinput');
98 var form = $('input.fileinput');
99 form.replaceWith(form.clone(true));
99 form.replaceWith(form.clone(true));
100 return false;
100 return false;
101 };
101 };
102
102
103 NotebookList.prototype.clear_list = function () {
103 NotebookList.prototype.clear_list = function () {
104 this.element.children('.list_item').remove();
104 this.element.children('.list_item').remove();
105 };
105 };
106
106
107 NotebookList.prototype.load_sessions = function(){
107 NotebookList.prototype.load_sessions = function(){
108 var that = this;
108 var that = this;
109 var settings = {
109 var settings = {
110 processData : false,
110 processData : false,
111 cache : false,
111 cache : false,
112 type : "GET",
112 type : "GET",
113 dataType : "json",
113 dataType : "json",
114 success : $.proxy(that.sessions_loaded, this)
114 success : $.proxy(that.sessions_loaded, this)
115 };
115 };
116 var url = this.baseProjectUrl() + 'api/sessions';
116 var url = this.baseProjectUrl() + 'api/sessions';
117 $.ajax(url,settings);
117 $.ajax(url,settings);
118 };
118 };
119
119
120
120
121 NotebookList.prototype.sessions_loaded = function(data){
121 NotebookList.prototype.sessions_loaded = function(data){
122 this.sessions = {};
122 this.sessions = {};
123 var len = data.length;
123 var len = data.length;
124 if (len > 0) {
124 if (len > 0) {
125 for (var i=0; i<len; i++) {
125 for (var i=0; i<len; i++) {
126 var nb_path;
126 var nb_path;
127 if (!data[i].notebook.path) {
127 if (!data[i].notebook.path) {
128 nb_path = data[i].notebook.name;
128 nb_path = data[i].notebook.name;
129 }
129 }
130 else {
130 else {
131 nb_path = utils.url_path_join(
131 nb_path = utils.url_path_join(
132 data[i].notebook.path,
132 data[i].notebook.path,
133 data[i].notebook.name
133 data[i].notebook.name
134 );
134 );
135 }
135 }
136 this.sessions[nb_path] = data[i].id;
136 this.sessions[nb_path] = data[i].id;
137 }
137 }
138 }
138 }
139 this.load_list();
139 this.load_list();
140 };
140 };
141
141
142 NotebookList.prototype.load_list = function () {
142 NotebookList.prototype.load_list = function () {
143 var that = this;
143 var that = this;
144 var settings = {
144 var settings = {
145 processData : false,
145 processData : false,
146 cache : false,
146 cache : false,
147 type : "GET",
147 type : "GET",
148 dataType : "json",
148 dataType : "json",
149 success : $.proxy(this.list_loaded, this),
149 success : $.proxy(this.list_loaded, this),
150 error : $.proxy( function(){
150 error : $.proxy( function(){
151 that.list_loaded([], null, null, {msg:"Error connecting to server."});
151 that.list_loaded([], null, null, {msg:"Error connecting to server."});
152 },this)
152 },this)
153 };
153 };
154
154
155 var url = utils.url_join_encode(
155 var url = utils.url_join_encode(
156 this.baseProjectUrl(),
156 this.baseProjectUrl(),
157 'api',
157 'api',
158 'notebooks',
158 'notebooks',
159 this.notebookPath()
159 this.notebookPath()
160 );
160 );
161 $.ajax(url, settings);
161 $.ajax(url, settings);
162 };
162 };
163
163
164
164
165 NotebookList.prototype.list_loaded = function (data, status, xhr, param) {
165 NotebookList.prototype.list_loaded = function (data, status, xhr, param) {
166 var message = 'Notebook list empty.';
166 var message = 'Notebook list empty.';
167 if (param !== undefined && param.msg) {
167 if (param !== undefined && param.msg) {
168 message = param.msg;
168 message = param.msg;
169 }
169 }
170 console.log(data);
170 var len = data.length;
171 var len = data.length;
171 this.clear_list();
172 this.clear_list();
172 if (len === 0) {
173 if (len === 0) {
173 $(this.new_notebook_item(0))
174 $(this.new_notebook_item(0))
174 .append(
175 .append(
175 $('<div style="margin:auto;text-align:center;color:grey"/>')
176 $('<div style="margin:auto;text-align:center;color:grey"/>')
176 .text(message)
177 .text(message)
177 );
178 );
178 }
179 }
179 for (var i=0; i<len; i++) {
180 for (var i=0; i<len; i++) {
180 var name = data[i].name;
181 var name = data[i].name;
181 var path = this.notebookPath();
182 var path = this.notebookPath();
182 var nbname = utils.splitext(name)[0];
183 var nbname = utils.splitext(name)[0];
183 var item = this.new_notebook_item(i);
184 var item = this.new_notebook_item(i);
184 this.add_link(path, nbname, item);
185 this.add_link(path, nbname, item);
185 name = utils.url_path_join(path, name);
186 name = utils.url_path_join(path, name);
186 if(this.sessions[name] === undefined){
187 if(this.sessions[name] === undefined){
187 this.add_delete_button(item);
188 this.add_delete_button(item);
188 } else {
189 } else {
189 this.add_shutdown_button(item,this.sessions[name]);
190 this.add_shutdown_button(item,this.sessions[name]);
190 }
191 }
191 }
192 }
192 };
193 };
193
194
194
195
195 NotebookList.prototype.new_notebook_item = function (index) {
196 NotebookList.prototype.new_notebook_item = function (index) {
196 var item = $('<div/>').addClass("list_item").addClass("row-fluid");
197 var item = $('<div/>').addClass("list_item").addClass("row-fluid");
197 // item.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
198 // item.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
198 // item.css('border-top-style','none');
199 // item.css('border-top-style','none');
199 item.append($("<div/>").addClass("span12").append(
200 item.append($("<div/>").addClass("span12").append(
200 $("<a/>").addClass("item_link").append(
201 $("<a/>").addClass("item_link").append(
201 $("<span/>").addClass("item_name")
202 $("<span/>").addClass("item_name")
202 )
203 )
203 ).append(
204 ).append(
204 $('<div/>').addClass("item_buttons btn-group pull-right")
205 $('<div/>').addClass("item_buttons btn-group pull-right")
205 ));
206 ));
206
207
207 if (index === -1) {
208 if (index === -1) {
208 this.element.append(item);
209 this.element.append(item);
209 } else {
210 } else {
210 this.element.children().eq(index).after(item);
211 this.element.children().eq(index).after(item);
211 }
212 }
212 return item;
213 return item;
213 };
214 };
214
215
215
216
216 NotebookList.prototype.add_link = function (path, nbname, item) {
217 NotebookList.prototype.add_link = function (path, nbname, item) {
217 item.data('nbname', nbname);
218 item.data('nbname', nbname);
218 item.data('path', path);
219 item.data('path', path);
219 item.find(".item_name").text(nbname);
220 item.find(".item_name").text(nbname);
220 item.find("a.item_link")
221 item.find("a.item_link")
221 .attr('href',
222 .attr('href',
222 utils.url_join_encode(
223 utils.url_join_encode(
223 this.baseProjectUrl(),
224 this.baseProjectUrl(),
224 "notebooks",
225 "notebooks",
225 path,
226 path,
226 nbname + ".ipynb"
227 nbname + ".ipynb"
227 )
228 )
228 ).attr('target','_blank');
229 ).attr('target','_blank');
229 };
230 };
230
231
231
232
232 NotebookList.prototype.add_name_input = function (nbname, item) {
233 NotebookList.prototype.add_name_input = function (nbname, item) {
233 item.data('nbname', nbname);
234 item.data('nbname', nbname);
234 item.find(".item_name").empty().append(
235 item.find(".item_name").empty().append(
235 $('<input/>')
236 $('<input/>')
236 .addClass("nbname_input")
237 .addClass("nbname_input")
237 .attr('value', nbname)
238 .attr('value', nbname)
238 .attr('size', '30')
239 .attr('size', '30')
239 .attr('type', 'text')
240 .attr('type', 'text')
240 );
241 );
241 };
242 };
242
243
243
244
244 NotebookList.prototype.add_notebook_data = function (data, item) {
245 NotebookList.prototype.add_notebook_data = function (data, item) {
245 item.data('nbdata', data);
246 item.data('nbdata', data);
246 };
247 };
247
248
248
249
249 NotebookList.prototype.add_shutdown_button = function (item, session) {
250 NotebookList.prototype.add_shutdown_button = function (item, session) {
250 var that = this;
251 var that = this;
251 var shutdown_button = $("<button/>").text("Shutdown").addClass("btn btn-mini").
252 var shutdown_button = $("<button/>").text("Shutdown").addClass("btn btn-mini").
252 click(function (e) {
253 click(function (e) {
253 var settings = {
254 var settings = {
254 processData : false,
255 processData : false,
255 cache : false,
256 cache : false,
256 type : "DELETE",
257 type : "DELETE",
257 dataType : "json",
258 dataType : "json",
258 success : function () {
259 success : function () {
259 that.load_sessions();
260 that.load_sessions();
260 }
261 }
261 };
262 };
262 var url = utils.url_join_encode(
263 var url = utils.url_join_encode(
263 that.baseProjectUrl(),
264 that.baseProjectUrl(),
264 'api/sessions',
265 'api/sessions',
265 session
266 session
266 );
267 );
267 $.ajax(url, settings);
268 $.ajax(url, settings);
268 return false;
269 return false;
269 });
270 });
270 // var new_buttons = item.find('a'); // shutdown_button;
271 // var new_buttons = item.find('a'); // shutdown_button;
271 item.find(".item_buttons").text("").append(shutdown_button);
272 item.find(".item_buttons").text("").append(shutdown_button);
272 };
273 };
273
274
274 NotebookList.prototype.add_delete_button = function (item) {
275 NotebookList.prototype.add_delete_button = function (item) {
275 var new_buttons = $('<span/>').addClass("btn-group pull-right");
276 var new_buttons = $('<span/>').addClass("btn-group pull-right");
276 var notebooklist = this;
277 var notebooklist = this;
277 var delete_button = $("<button/>").text("Delete").addClass("btn btn-mini").
278 var delete_button = $("<button/>").text("Delete").addClass("btn btn-mini").
278 click(function (e) {
279 click(function (e) {
279 // $(this) is the button that was clicked.
280 // $(this) is the button that was clicked.
280 var that = $(this);
281 var that = $(this);
281 // We use the nbname and notebook_id from the parent notebook_item element's
282 // We use the nbname and notebook_id from the parent notebook_item element's
282 // data because the outer scopes values change as we iterate through the loop.
283 // data because the outer scopes values change as we iterate through the loop.
283 var parent_item = that.parents('div.list_item');
284 var parent_item = that.parents('div.list_item');
284 var nbname = parent_item.data('nbname');
285 var nbname = parent_item.data('nbname');
285 var message = 'Are you sure you want to permanently delete the notebook: ' + nbname + '?';
286 var message = 'Are you sure you want to permanently delete the notebook: ' + nbname + '?';
286 IPython.dialog.modal({
287 IPython.dialog.modal({
287 title : "Delete notebook",
288 title : "Delete notebook",
288 body : message,
289 body : message,
289 buttons : {
290 buttons : {
290 Delete : {
291 Delete : {
291 class: "btn-danger",
292 class: "btn-danger",
292 click: function() {
293 click: function() {
293 var settings = {
294 var settings = {
294 processData : false,
295 processData : false,
295 cache : false,
296 cache : false,
296 type : "DELETE",
297 type : "DELETE",
297 dataType : "json",
298 dataType : "json",
298 success : function (data, status, xhr) {
299 success : function (data, status, xhr) {
299 parent_item.remove();
300 parent_item.remove();
300 }
301 }
301 };
302 };
302 var url = utils.url_join_encode(
303 var url = utils.url_join_encode(
303 notebooklist.baseProjectUrl(),
304 notebooklist.baseProjectUrl(),
304 'api/notebooks',
305 'api/notebooks',
305 notebooklist.notebookPath(),
306 notebooklist.notebookPath(),
306 nbname + '.ipynb'
307 nbname + '.ipynb'
307 );
308 );
308 $.ajax(url, settings);
309 $.ajax(url, settings);
309 }
310 }
310 },
311 },
311 Cancel : {}
312 Cancel : {}
312 }
313 }
313 });
314 });
314 return false;
315 return false;
315 });
316 });
316 item.find(".item_buttons").text("").append(delete_button);
317 item.find(".item_buttons").text("").append(delete_button);
317 };
318 };
318
319
319
320
320 NotebookList.prototype.add_upload_button = function (item) {
321 NotebookList.prototype.add_upload_button = function (item) {
321 var that = this;
322 var that = this;
322 var upload_button = $('<button/>').text("Upload")
323 var upload_button = $('<button/>').text("Upload")
323 .addClass('btn btn-primary btn-mini upload_button')
324 .addClass('btn btn-primary btn-mini upload_button')
324 .click(function (e) {
325 .click(function (e) {
325 var nbname = item.find('.item_name > input').val();
326 var nbname = item.find('.item_name > input').val();
326 var path = that.notebookPath();
327 var path = that.notebookPath();
327 var nbdata = item.data('nbdata');
328 var nbdata = item.data('nbdata');
328 var content_type = 'application/json';
329 var content_type = 'application/json';
329 var model = {
330 var model = {
330 content : JSON.parse(nbdata),
331 content : JSON.parse(nbdata),
331 };
332 };
332 var settings = {
333 var settings = {
333 processData : false,
334 processData : false,
334 cache : false,
335 cache : false,
335 type : 'PUT',
336 type : 'PUT',
336 dataType : 'json',
337 dataType : 'json',
337 data : JSON.stringify(model),
338 data : JSON.stringify(model),
338 headers : {'Content-Type': content_type},
339 headers : {'Content-Type': content_type},
339 success : function (data, status, xhr) {
340 success : function (data, status, xhr) {
340 that.add_link(path, nbname, item);
341 that.add_link(path, nbname, item);
341 that.add_delete_button(item);
342 that.add_delete_button(item);
342 },
343 },
343 error : function (data, status, xhr) {
344 error : function (data, status, xhr) {
344 console.log(data, status);
345 console.log(data, status);
345 }
346 }
346 };
347 };
347
348
348 var url = utils.url_join_encode(
349 var url = utils.url_join_encode(
349 that.baseProjectUrl(),
350 that.baseProjectUrl(),
350 'api/notebooks',
351 'api/notebooks',
351 that.notebookPath(),
352 that.notebookPath(),
352 nbname + '.ipynb'
353 nbname + '.ipynb'
353 );
354 );
354 $.ajax(url, settings);
355 $.ajax(url, settings);
355 return false;
356 return false;
356 });
357 });
357 var cancel_button = $('<button/>').text("Cancel")
358 var cancel_button = $('<button/>').text("Cancel")
358 .addClass("btn btn-mini")
359 .addClass("btn btn-mini")
359 .click(function (e) {
360 .click(function (e) {
360 console.log('cancel click');
361 console.log('cancel click');
361 item.remove();
362 item.remove();
362 return false;
363 return false;
363 });
364 });
364 item.find(".item_buttons").empty()
365 item.find(".item_buttons").empty()
365 .append(upload_button)
366 .append(upload_button)
366 .append(cancel_button);
367 .append(cancel_button);
367 };
368 };
368
369
369
370
370 NotebookList.prototype.new_notebook = function(){
371 NotebookList.prototype.new_notebook = function(){
371 var path = this.notebookPath();
372 var path = this.notebookPath();
372 var base_project_url = this.baseProjectUrl();
373 var base_project_url = this.baseProjectUrl();
373 var settings = {
374 var settings = {
374 processData : false,
375 processData : false,
375 cache : false,
376 cache : false,
376 type : "POST",
377 type : "POST",
377 dataType : "json",
378 dataType : "json",
378 async : false,
379 async : false,
379 success : function (data, status, xhr) {
380 success : function (data, status, xhr) {
380 var notebook_name = data.name;
381 var notebook_name = data.name;
381 window.open(
382 window.open(
382 utils.url_join_encode(
383 utils.url_join_encode(
383 base_project_url,
384 base_project_url,
384 'notebooks',
385 'notebooks',
385 path,
386 path,
386 notebook_name),
387 notebook_name),
387 '_blank'
388 '_blank'
388 );
389 );
389 }
390 }
390 };
391 };
391 var url = utils.url_join_encode(
392 var url = utils.url_join_encode(
392 base_project_url,
393 base_project_url,
393 'api/notebooks',
394 'api/notebooks',
394 path
395 path
395 );
396 );
396 $.ajax(url, settings);
397 $.ajax(url, settings);
397 };
398 };
398
399
399 IPython.NotebookList = NotebookList;
400 IPython.NotebookList = NotebookList;
400
401
401 return IPython;
402 return IPython;
402
403
403 }(IPython));
404 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now