##// END OF EJS Templates
Implemented delete functionality in nb browser....
Brian E. Granger -
Show More
@@ -1,199 +1,198
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.txt, distributed as part of this software.
5 # the file COPYING.txt, distributed as part of this software.
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Imports
9 # Imports
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 import datetime
12 import datetime
13 import os
13 import os
14 import uuid
14 import uuid
15
15
16 from tornado import web
16 from tornado import web
17
17
18 from IPython.config.configurable import Configurable
18 from IPython.config.configurable import Configurable
19 from IPython.nbformat import current
19 from IPython.nbformat import current
20 from IPython.utils.traitlets import Unicode, List, Dict
20 from IPython.utils.traitlets import Unicode, List, Dict
21
21
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27
27
28 class NotebookManager(Configurable):
28 class NotebookManager(Configurable):
29
29
30 notebook_dir = Unicode(os.getcwd())
30 notebook_dir = Unicode(os.getcwd())
31 filename_ext = Unicode(u'.ipynb')
31 filename_ext = Unicode(u'.ipynb')
32 allowed_formats = List([u'json',u'xml',u'py'])
32 allowed_formats = List([u'json',u'xml',u'py'])
33
33
34 # Map notebook_ids to notebook names
34 # Map notebook_ids to notebook names
35 mapping = Dict()
35 mapping = Dict()
36 # Map notebook names to notebook_ids
36 # Map notebook names to notebook_ids
37 rev_mapping = Dict()
37 rev_mapping = Dict()
38
38
39 def list_notebooks(self):
39 def list_notebooks(self):
40 """List all notebooks in the notebook dir.
40 """List all notebooks in the notebook dir.
41
41
42 This returns a list of dicts of the form::
42 This returns a list of dicts of the form::
43
43
44 dict(notebook_id=notebook,name=name)
44 dict(notebook_id=notebook,name=name)
45 """
45 """
46 names = os.listdir(self.notebook_dir)
46 names = os.listdir(self.notebook_dir)
47 names = [name.split(u'.')[0] \
47 names = [name.split(u'.')[0] \
48 for name in names if name.endswith(self.filename_ext)]
48 for name in names if name.endswith(self.filename_ext)]
49 print names
50 data = []
49 data = []
51 for name in names:
50 for name in names:
52 if name not in self.rev_mapping:
51 if name not in self.rev_mapping:
53 notebook_id = self.new_notebook_id(name)
52 notebook_id = self.new_notebook_id(name)
54 else:
53 else:
55 notebook_id = self.rev_mapping[name]
54 notebook_id = self.rev_mapping[name]
56 data.append(dict(notebook_id=notebook_id,name=name))
55 data.append(dict(notebook_id=notebook_id,name=name))
57 data = sorted(data, key=lambda item: item['name'])
56 data = sorted(data, key=lambda item: item['name'])
58 return data
57 return data
59
58
60 def new_notebook_id(self, name):
59 def new_notebook_id(self, name):
61 """Generate a new notebook_id for a name and store its mappings."""
60 """Generate a new notebook_id for a name and store its mappings."""
62 notebook_id = unicode(uuid.uuid4())
61 notebook_id = unicode(uuid.uuid4())
63 self.mapping[notebook_id] = name
62 self.mapping[notebook_id] = name
64 self.rev_mapping[name] = notebook_id
63 self.rev_mapping[name] = notebook_id
65 return notebook_id
64 return notebook_id
66
65
67 def delete_notebook_id(self, notebook_id):
66 def delete_notebook_id(self, notebook_id):
68 """Delete a notebook's id only. This doesn't delete the actual notebook."""
67 """Delete a notebook's id only. This doesn't delete the actual notebook."""
69 name = self.mapping[notebook_id]
68 name = self.mapping[notebook_id]
70 del self.mapping[notebook_id]
69 del self.mapping[notebook_id]
71 del self.rev_mapping[name]
70 del self.rev_mapping[name]
72
71
73 def notebook_exists(self, notebook_id):
72 def notebook_exists(self, notebook_id):
74 """Does a notebook exist?"""
73 """Does a notebook exist?"""
75 if notebook_id not in self.mapping:
74 if notebook_id not in self.mapping:
76 return False
75 return False
77 path = self.get_path_by_name(self.mapping[notebook_id])
76 path = self.get_path_by_name(self.mapping[notebook_id])
78 if not os.path.isfile(path):
77 if not os.path.isfile(path):
79 return False
78 return False
80 return True
79 return True
81
80
82 def find_path(self, notebook_id):
81 def find_path(self, notebook_id):
83 """Return a full path to a notebook given its notebook_id."""
82 """Return a full path to a notebook given its notebook_id."""
84 try:
83 try:
85 name = self.mapping[notebook_id]
84 name = self.mapping[notebook_id]
86 except KeyError:
85 except KeyError:
87 raise web.HTTPError(404)
86 raise web.HTTPError(404)
88 return self.get_path_by_name(name)
87 return self.get_path_by_name(name)
89
88
90 def get_path_by_name(self, name):
89 def get_path_by_name(self, name):
91 """Return a full path to a notebook given its name."""
90 """Return a full path to a notebook given its name."""
92 filename = name + self.filename_ext
91 filename = name + self.filename_ext
93 path = os.path.join(self.notebook_dir, filename)
92 path = os.path.join(self.notebook_dir, filename)
94 return path
93 return path
95
94
96 def get_notebook(self, notebook_id, format=u'json'):
95 def get_notebook(self, notebook_id, format=u'json'):
97 """Get the representation of a notebook in format by notebook_id."""
96 """Get the representation of a notebook in format by notebook_id."""
98 format = unicode(format)
97 format = unicode(format)
99 if format not in self.allowed_formats:
98 if format not in self.allowed_formats:
100 raise web.HTTPError(415)
99 raise web.HTTPError(415)
101 last_modified, nb = self.get_notebook_object(notebook_id)
100 last_modified, nb = self.get_notebook_object(notebook_id)
102 data = current.writes(nb, format)
101 data = current.writes(nb, format)
103 name = nb.get('name','notebook')
102 name = nb.get('name','notebook')
104 return last_modified, name, data
103 return last_modified, name, data
105
104
106 def get_notebook_object(self, notebook_id):
105 def get_notebook_object(self, notebook_id):
107 """Get the NotebookNode representation of a notebook by notebook_id."""
106 """Get the NotebookNode representation of a notebook by notebook_id."""
108 path = self.find_path(notebook_id)
107 path = self.find_path(notebook_id)
109 if not os.path.isfile(path):
108 if not os.path.isfile(path):
110 raise web.HTTPError(404)
109 raise web.HTTPError(404)
111 info = os.stat(path)
110 info = os.stat(path)
112 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
111 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
113 try:
112 try:
114 with open(path,'r') as f:
113 with open(path,'r') as f:
115 s = f.read()
114 s = f.read()
116 try:
115 try:
117 # v2 and later have xml in the .ipynb files.
116 # v2 and later have xml in the .ipynb files.
118 nb = current.reads(s, 'xml')
117 nb = current.reads(s, 'xml')
119 except:
118 except:
120 # v1 had json in the .ipynb files.
119 # v1 had json in the .ipynb files.
121 nb = current.reads(s, 'json')
120 nb = current.reads(s, 'json')
122 # v1 notebooks don't have a name field, so use the filename.
121 # v1 notebooks don't have a name field, so use the filename.
123 nb.name = os.path.split(path)[-1].split(u'.')[0]
122 nb.name = os.path.split(path)[-1].split(u'.')[0]
124 except:
123 except:
125 raise web.HTTPError(404)
124 raise web.HTTPError(404)
126 return last_modified, nb
125 return last_modified, nb
127
126
128 def save_new_notebook(self, data, format=u'json'):
127 def save_new_notebook(self, data, format=u'json'):
129 """Save a new notebook and return its notebook_id."""
128 """Save a new notebook and return its notebook_id."""
130 if format not in self.allowed_formats:
129 if format not in self.allowed_formats:
131 raise web.HTTPError(415)
130 raise web.HTTPError(415)
132 try:
131 try:
133 nb = current.reads(data, format)
132 nb = current.reads(data, format)
134 except:
133 except:
135 raise web.HTTPError(400)
134 raise web.HTTPError(400)
136 try:
135 try:
137 name = nb.name
136 name = nb.name
138 except AttributeError:
137 except AttributeError:
139 raise web.HTTPError(400)
138 raise web.HTTPError(400)
140 notebook_id = self.new_notebook_id(name)
139 notebook_id = self.new_notebook_id(name)
141 self.save_notebook_object(notebook_id, nb)
140 self.save_notebook_object(notebook_id, nb)
142 return notebook_id
141 return notebook_id
143
142
144 def save_notebook(self, notebook_id, data, format=u'json'):
143 def save_notebook(self, notebook_id, data, format=u'json'):
145 """Save an existing notebook by notebook_id."""
144 """Save an existing notebook by notebook_id."""
146 if format not in self.allowed_formats:
145 if format not in self.allowed_formats:
147 raise web.HTTPError(415)
146 raise web.HTTPError(415)
148 try:
147 try:
149 nb = current.reads(data, format)
148 nb = current.reads(data, format)
150 except:
149 except:
151 raise web.HTTPError(400)
150 raise web.HTTPError(400)
152 self.save_notebook_object(notebook_id, nb)
151 self.save_notebook_object(notebook_id, nb)
153
152
154 def save_notebook_object(self, notebook_id, nb):
153 def save_notebook_object(self, notebook_id, nb):
155 """Save an existing notebook object by notebook_id."""
154 """Save an existing notebook object by notebook_id."""
156 if notebook_id not in self.mapping:
155 if notebook_id not in self.mapping:
157 raise web.HTTPError(404)
156 raise web.HTTPError(404)
158 old_name = self.mapping[notebook_id]
157 old_name = self.mapping[notebook_id]
159 try:
158 try:
160 new_name = nb.name
159 new_name = nb.name
161 except AttributeError:
160 except AttributeError:
162 raise web.HTTPError(400)
161 raise web.HTTPError(400)
163 path = self.get_path_by_name(new_name)
162 path = self.get_path_by_name(new_name)
164 try:
163 try:
165 with open(path,'w') as f:
164 with open(path,'w') as f:
166 current.write(nb, f, u'xml')
165 current.write(nb, f, u'xml')
167 except:
166 except:
168 raise web.HTTPError(400)
167 raise web.HTTPError(400)
169 if old_name != new_name:
168 if old_name != new_name:
170 old_path = self.get_path_by_name(old_name)
169 old_path = self.get_path_by_name(old_name)
171 if os.path.isfile(old_path):
170 if os.path.isfile(old_path):
172 os.unlink(old_path)
171 os.unlink(old_path)
173 self.mapping[notebook_id] = new_name
172 self.mapping[notebook_id] = new_name
174 self.rev_mapping[new_name] = notebook_id
173 self.rev_mapping[new_name] = notebook_id
175
174
176 def delete_notebook(self, notebook_id):
175 def delete_notebook(self, notebook_id):
177 """Delete notebook by notebook_id."""
176 """Delete notebook by notebook_id."""
178 path = self.find_path(notebook_id)
177 path = self.find_path(notebook_id)
179 if not os.path.isfile(path):
178 if not os.path.isfile(path):
180 raise web.HTTPError(404)
179 raise web.HTTPError(404)
181 os.unlink(path)
180 os.unlink(path)
182 self.delete_notebook_id(notebook_id)
181 self.delete_notebook_id(notebook_id)
183
182
184 def new_notebook(self):
183 def new_notebook(self):
185 """Create a new notebook and returns its notebook_id."""
184 """Create a new notebook and returns its notebook_id."""
186 i = 0
185 i = 0
187 while True:
186 while True:
188 name = u'Untitled%i' % i
187 name = u'Untitled%i' % i
189 path = self.get_path_by_name(name)
188 path = self.get_path_by_name(name)
190 if not os.path.isfile(path):
189 if not os.path.isfile(path):
191 break
190 break
192 else:
191 else:
193 i = i+1
192 i = i+1
194 notebook_id = self.new_notebook_id(name)
193 notebook_id = self.new_notebook_id(name)
195 nb = current.new_notebook(name=name, id=notebook_id)
194 nb = current.new_notebook(name=name, id=notebook_id)
196 with open(path,'w') as f:
195 with open(path,'w') as f:
197 current.write(nb, f, u'xml')
196 current.write(nb, f, u'xml')
198 return notebook_id
197 return notebook_id
199
198
@@ -1,63 +1,108
1
1
2 //============================================================================
2 //============================================================================
3 // Cell
3 // Cell
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var NotebookList = function (selector) {
8 var NotebookList = function (selector) {
9 this.selector = selector;
9 this.selector = selector;
10 if (this.selector !== undefined) {
10 if (this.selector !== undefined) {
11 this.element = $(selector);
11 this.element = $(selector);
12 this.style();
12 this.style();
13 this.bind_events();
13 this.bind_events();
14 }
14 }
15 };
15 };
16
16
17 NotebookList.prototype.style = function () {
17 NotebookList.prototype.style = function () {
18 this.element.addClass('ui-widget ui-widget-content');
18 this.element.addClass('ui-widget ui-widget-content');
19 $('div#project_name').addClass('ui-widget ui-widget-header');
19 $('div#project_name').addClass('ui-widget ui-widget-header');
20 };
20 };
21
21
22
22
23 NotebookList.prototype.bind_events = function () {
23 NotebookList.prototype.bind_events = function () {
24
24
25 };
25 };
26
26
27
27
28 NotebookList.prototype.load_list = function () {
28 NotebookList.prototype.load_list = function () {
29 var settings = {
29 var settings = {
30 processData : false,
30 processData : false,
31 cache : false,
31 cache : false,
32 type : "GET",
32 type : "GET",
33 dataType : "json",
33 dataType : "json",
34 success : $.proxy(this.list_loaded,this)
34 success : $.proxy(this.list_loaded,this)
35 };
35 };
36 $.ajax("/notebooks", settings);
36 $.ajax("/notebooks", settings);
37 };
37 };
38
38
39
39
40 NotebookList.prototype.list_loaded = function (data, status, xhr) {
40 NotebookList.prototype.list_loaded = function (data, status, xhr) {
41 var len = data.length;
41 var len = data.length;
42 for (var i=0; i<len; i++) {
42 for (var i=0; i<len; i++) {
43 var div = $('<div/>').addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix');
43 var notebook_id = data[i].notebook_id;
44 var nbname = $('<span/>').addClass('item_name').append(
44 var nbname = data[i].name;
45 $('<a/>').attr('href','/'+data[i].notebook_id).
45
46 var item = $('<div/>');
47 item.addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix');
48 var item_name = $('<span/>').addClass('item_name').append(
49 $('<a/>').attr('href','/'+notebook_id).
46 attr('target','_blank').
50 attr('target','_blank').
47 text(data[i].name)
51 text(nbname)
48 );
52 );
49 var buttons = $('<span/>').addClass('item_buttons').append(
53 // Store the nbname and notebook_id on the item for later usage. We have to do this
50 $('<button>Delete</button>').button()
54 // because the loop over elements changes the values of the local nbname and notebook_id
51 )
55 // variables.
52 div.append(nbname).append(buttons);
56 item.data('notebook_id',notebook_id);
53 this.element.append(div);
57 item.data('nbname',nbname);
58
59 var buttons = $('<span/>').addClass('item_buttons');
60 var delete_button = $('<button>Delete</button>').button().
61 click(function (e) {
62 // $(this) is the button that was clicked.
63 var that = $(this);
64 // We use the nbname and notebook_id from the parent notebook_item element's
65 // data because the outer scopes values change as we iterate through the loop.
66 var parent_item = that.parents('div.notebook_item');
67 var nbname = parent_item.data('nbname');
68 var notebook_id = parent_item.data('notebook_id');
69 var dialog = $('<div/>');
70 dialog.html('Are you sure you want to permanently delete the notebook: ' + nbname + '?');
71 parent_item.append(dialog);
72 dialog.dialog({
73 resizable: false,
74 modal: true,
75 title: "Delete notebook",
76 buttons : {
77 "Delete": function () {
78 var settings = {
79 processData : false,
80 cache : false,
81 type : "DELETE",
82 dataType : "json",
83 success : function (data, status, xhr) {
84 parent_item.remove();
85 }
86 };
87 $.ajax("/notebooks/" + notebook_id, settings);
88 $(this).dialog('close');
89 },
90 "Cancel": function () {
91 $(this).dialog('close');
92 }
93 }
94 });
95 });
96 buttons.append(delete_button);
97 item.append(item_name).append(buttons);
98 this.element.append(item);
54 }
99 }
55 };
100 };
56
101
57
102
58 IPython.NotebookList = NotebookList;
103 IPython.NotebookList = NotebookList;
59
104
60 return IPython;
105 return IPython;
61
106
62 }(IPython));
107 }(IPython));
63
108
General Comments 0
You need to be logged in to leave comments. Login now