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