##// END OF EJS Templates
Fixing bug in new metadata implementation.
Brian E. Granger -
Show More
@@ -1,218 +1,219 b''
1 1 """A notebook manager that uses the local file system for storage.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import datetime
20 20 import os
21 21 import uuid
22 22 import glob
23 23
24 24 from tornado import web
25 25
26 26 from IPython.config.configurable import LoggingConfigurable
27 27 from IPython.nbformat import current
28 28 from IPython.utils.traitlets import Unicode, List, Dict
29 29
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Code
33 33 #-----------------------------------------------------------------------------
34 34
35 35
36 36 class NotebookManager(LoggingConfigurable):
37 37
38 38 notebook_dir = Unicode(os.getcwd(), config=True, help="""
39 39 The directory to use for notebooks.
40 40 """)
41 41 filename_ext = Unicode(u'.ipynb')
42 42 allowed_formats = List([u'json',u'py'])
43 43
44 44 # Map notebook_ids to notebook names
45 45 mapping = Dict()
46 46 # Map notebook names to notebook_ids
47 47 rev_mapping = Dict()
48 48
49 49 def list_notebooks(self):
50 50 """List all notebooks in the notebook dir.
51 51
52 52 This returns a list of dicts of the form::
53 53
54 54 dict(notebook_id=notebook,name=name)
55 55 """
56 56 names = glob.glob(os.path.join(self.notebook_dir,
57 57 '*' + self.filename_ext))
58 58 names = [os.path.splitext(os.path.basename(name))[0]
59 59 for name in names]
60 60
61 61 data = []
62 62 for name in names:
63 63 if name not in self.rev_mapping:
64 64 notebook_id = self.new_notebook_id(name)
65 65 else:
66 66 notebook_id = self.rev_mapping[name]
67 67 data.append(dict(notebook_id=notebook_id,name=name))
68 68 data = sorted(data, key=lambda item: item['name'])
69 69 return data
70 70
71 71 def new_notebook_id(self, name):
72 72 """Generate a new notebook_id for a name and store its mappings."""
73 73 notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
74 74 'file://'+self.get_path_by_name(name).encode('utf-8')))
75 75 self.mapping[notebook_id] = name
76 76 self.rev_mapping[name] = notebook_id
77 77 return notebook_id
78 78
79 79 def delete_notebook_id(self, notebook_id):
80 80 """Delete a notebook's id only. This doesn't delete the actual notebook."""
81 81 name = self.mapping[notebook_id]
82 82 del self.mapping[notebook_id]
83 83 del self.rev_mapping[name]
84 84
85 85 def notebook_exists(self, notebook_id):
86 86 """Does a notebook exist?"""
87 87 if notebook_id not in self.mapping:
88 88 return False
89 89 path = self.get_path_by_name(self.mapping[notebook_id])
90 90 return os.path.isfile(path)
91 91
92 92 def find_path(self, notebook_id):
93 93 """Return a full path to a notebook given its notebook_id."""
94 94 try:
95 95 name = self.mapping[notebook_id]
96 96 except KeyError:
97 97 raise web.HTTPError(404)
98 98 return self.get_path_by_name(name)
99 99
100 100 def get_path_by_name(self, name):
101 101 """Return a full path to a notebook given its name."""
102 102 filename = name + self.filename_ext
103 103 path = os.path.join(self.notebook_dir, filename)
104 104 return path
105 105
106 106 def get_notebook(self, notebook_id, format=u'json'):
107 107 """Get the representation of a notebook in format by notebook_id."""
108 108 format = unicode(format)
109 109 if format not in self.allowed_formats:
110 110 raise web.HTTPError(415)
111 111 last_modified, nb = self.get_notebook_object(notebook_id)
112 112 data = current.writes(nb, format)
113 113 name = nb.get('name','notebook')
114 114 return last_modified, name, data
115 115
116 116 def get_notebook_object(self, notebook_id):
117 117 """Get the NotebookNode representation of a notebook by notebook_id."""
118 118 path = self.find_path(notebook_id)
119 119 if not os.path.isfile(path):
120 120 raise web.HTTPError(404)
121 121 info = os.stat(path)
122 122 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
123 123 with open(path,'r') as f:
124 124 s = f.read()
125 125 try:
126 126 # v1 and v2 and json in the .ipynb files.
127 127 nb = current.reads(s, u'json')
128 128 except:
129 129 raise web.HTTPError(404)
130 130 if 'name' not in nb:
131 131 nb.name = os.path.split(path)[-1].split(u'.')[0]
132 132 return last_modified, nb
133 133
134 134 def save_new_notebook(self, data, name=None, format=u'json'):
135 135 """Save a new notebook and return its notebook_id.
136 136
137 137 If a name is passed in, it overrides any values in the notebook data
138 138 and the value in the data is updated to use that value.
139 139 """
140 140 if format not in self.allowed_formats:
141 141 raise web.HTTPError(415)
142 142
143 143 try:
144 144 nb = current.reads(data, format)
145 145 except:
146 146 raise web.HTTPError(400)
147 147
148 148 if name is None:
149 149 try:
150 150 name = nb.metadata.name
151 151 except AttributeError:
152 152 raise web.HTTPError(400)
153 153 nb.metadata.name = name
154 154
155 155 notebook_id = self.new_notebook_id(name)
156 156 self.save_notebook_object(notebook_id, nb)
157 157 return notebook_id
158 158
159 159 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
160 160 """Save an existing notebook by notebook_id."""
161 161 if format not in self.allowed_formats:
162 162 raise web.HTTPError(415)
163 163
164 164 try:
165 165 nb = current.reads(data, format)
166 166 except:
167 167 raise web.HTTPError(400)
168 168
169 169 if name is not None:
170 170 nb.metadata.name = name
171 171 self.save_notebook_object(notebook_id, nb)
172 172
173 173 def save_notebook_object(self, notebook_id, nb):
174 174 """Save an existing notebook object by notebook_id."""
175 175 if notebook_id not in self.mapping:
176 176 raise web.HTTPError(404)
177 177 old_name = self.mapping[notebook_id]
178 178 try:
179 179 new_name = nb.metadata.name
180 180 except AttributeError:
181 181 raise web.HTTPError(400)
182 182 path = self.get_path_by_name(new_name)
183 183 try:
184 184 with open(path,'w') as f:
185 185 current.write(nb, f, u'json')
186 186 except:
187 187 raise web.HTTPError(400)
188 188 if old_name != new_name:
189 189 old_path = self.get_path_by_name(old_name)
190 190 if os.path.isfile(old_path):
191 191 os.unlink(old_path)
192 192 self.mapping[notebook_id] = new_name
193 193 self.rev_mapping[new_name] = notebook_id
194 194
195 195 def delete_notebook(self, notebook_id):
196 196 """Delete notebook by notebook_id."""
197 197 path = self.find_path(notebook_id)
198 198 if not os.path.isfile(path):
199 199 raise web.HTTPError(404)
200 200 os.unlink(path)
201 201 self.delete_notebook_id(notebook_id)
202 202
203 203 def new_notebook(self):
204 204 """Create a new notebook and returns its notebook_id."""
205 205 i = 0
206 206 while True:
207 207 name = u'Untitled%i' % i
208 208 path = self.get_path_by_name(name)
209 209 if not os.path.isfile(path):
210 210 break
211 211 else:
212 212 i = i+1
213 213 notebook_id = self.new_notebook_id(name)
214 nb = current.new_notebook(name=name)
214 metadata = current.new_metadata(name=name)
215 nb = current.new_notebook(metadata=metadata)
215 216 with open(path,'w') as f:
216 217 current.write(nb, f, u'json')
217 218 return notebook_id
218 219
General Comments 0
You need to be logged in to leave comments. Login now