23 Error looking up/handling a page |
23 Error looking up/handling a page |
24 """ |
24 """ |
25 |
25 |
26 pass |
26 pass |
27 |
27 |
28 class PageList (object) : |
28 class PageInfo (object) : |
|
29 """ |
|
30 Contains metainformation about a page |
|
31 """ |
|
32 |
|
33 def __init__ (self, parent, name, title, children=None) : |
|
34 """ |
|
35 Initialize, children defaults to empty list |
|
36 """ |
|
37 |
|
38 # store |
|
39 self.parent = parent |
|
40 self.name = name |
|
41 self.title = title |
|
42 self.children = children if children else [] |
|
43 |
|
44 # no url get |
|
45 self._url = None |
|
46 |
|
47 def set_parent (self, parent) : |
|
48 """ |
|
49 Set a parent where non was set before |
|
50 """ |
|
51 |
|
52 assert self.parent is None |
|
53 |
|
54 self.parent = parent |
|
55 |
|
56 def add_child (self, child) : |
|
57 """ |
|
58 Add a PageInfo child |
|
59 """ |
|
60 |
|
61 self.children.append(child) |
|
62 |
|
63 def get_child (self, name) : |
|
64 """ |
|
65 Look up a child by name, returning None if not found |
|
66 """ |
|
67 |
|
68 return dict((c.name, c) for c in self.children).get(name) |
|
69 |
|
70 @property |
|
71 def url (self) : |
|
72 """ |
|
73 Build this page's URL |
|
74 """ |
|
75 |
|
76 # cached? |
|
77 if self._url : |
|
78 return self._url |
|
79 |
|
80 # collect URL segments in reverse order |
|
81 segments = [] |
|
82 |
|
83 # add empty segment if dir |
|
84 if self.children : |
|
85 segments.append('') |
|
86 |
|
87 # iterate over ancestry |
|
88 item = self |
|
89 |
|
90 # add all parent names, but not root's |
|
91 while item and item.parent : |
|
92 segments.append(item.name) |
|
93 |
|
94 item = item.parent |
|
95 |
|
96 # reverse segments |
|
97 segments.reverse() |
|
98 |
|
99 # join |
|
100 url = '/'.join(segments) |
|
101 |
|
102 # cache |
|
103 self._url = url |
|
104 |
|
105 # done |
|
106 return url |
|
107 |
|
108 class PageTree (object) : |
29 """ |
109 """ |
30 The list of pages |
110 The list of pages |
31 """ |
111 """ |
32 |
112 |
33 def __init__ (self) : |
113 def __init__ (self) : |
34 """ |
114 """ |
35 Loads the page list from the list file |
115 Empty PageList, must call load_page_list to initialize, once |
36 """ |
116 """ |
37 |
117 |
38 # initialize list of pages |
118 def _load (self, path=PAGE_LIST_FILE) : |
39 self.pages = [] |
|
40 |
|
41 # load from file |
|
42 self._load(PAGE_LIST_FILE) |
|
43 |
|
44 def _load (self, path) : |
|
45 """ |
119 """ |
46 Processes the lines in the given file |
120 Processes the lines in the given file |
47 """ |
121 """ |
|
122 |
|
123 # collect the page list |
|
124 pages = [] |
|
125 |
|
126 # stack of (indent, PageInfo) items |
|
127 stack = [] |
|
128 |
|
129 # the previous item processed, None for first one |
|
130 prev = None |
48 |
131 |
49 for line in open(path, 'rb') : |
132 for line in open(path, 'rb') : |
50 # ignore whitespace |
133 indent = 0 |
|
134 |
|
135 # count indent |
|
136 for char in line : |
|
137 # tabs break things |
|
138 assert char != '\t' |
|
139 |
|
140 # increment up to first non-space char |
|
141 if char == ' ' : |
|
142 indent += 1 |
|
143 |
|
144 elif char == ':' : |
|
145 indent = 0 |
|
146 break |
|
147 |
|
148 else : |
|
149 break |
|
150 |
|
151 # strip whitespace |
51 line = line.strip() |
152 line = line.strip() |
52 |
153 |
53 # ignore empty lines |
154 # ignore empty lines |
54 if not line : |
155 if not line : |
55 continue |
156 continue |
56 |
157 |
57 # parse line |
158 # parse line |
58 url, title = line.split(':') |
159 url, title = line.split(':') |
59 |
160 |
60 # add |
161 # remove whitespace |
61 self._add_item(url.strip(), title.strip()) |
162 url = url.strip() |
62 |
163 title = title.strip() |
63 def _add_item (self, url, title) : |
164 |
64 """ |
165 # create PageInfo item without parent |
65 Add item to pages list |
166 item = PageInfo(None, url, title) |
66 """ |
167 |
67 |
168 # are we the first item? |
68 # remove index from URL |
169 if not prev : |
69 if url.endswith('/index') or url == 'index' : |
170 assert url == '', "Page list must begin with root item" |
70 url = url[:-len('/index')] |
171 |
71 |
172 # root node does not have a parent |
72 self.pages.append((url, title)) |
173 parent = None |
73 |
174 |
74 def get_title (self, page) : |
175 # set root |
75 """ |
176 self.root = item |
76 Gets the title for the given page, or None if not found |
177 |
77 """ |
178 # tee hee hee |
78 |
179 self.root.add_child(self.root) |
79 return dict(self.pages).get(page) |
180 |
80 |
181 # initialize stack |
81 def get_siblings (self, page) : |
182 stack.append((0, self.root)) |
82 """ |
183 |
83 Gets the (url, title) tuple list of siblings (including the given page itself) for the given page |
184 else : |
84 """ |
185 # peek stack |
85 |
186 stack_indent, stack_parent = stack[-1] |
86 siblings = [] |
187 |
87 |
188 # new indent level? |
88 # parent url |
189 if indent > stack_indent : |
89 parent = os.path.split(page.url)[0] |
190 # set parent to previous item, and push new indent level + parent to stack |
90 |
191 parent = prev |
91 # how many segments in the page name |
192 |
92 segment_count = len(page.url.split('/')) |
193 # push new indent level + its parent |
93 |
194 stack.append((indent, parent)) |
94 # go through all pages |
195 |
95 for url, title in self.pages : |
196 # same indent level as previous |
96 # it's a sibling if the parent is the same, and the number of segments it the same |
197 elif indent == stack_indent : |
97 if url.startswith(parent) and len(url.split('/')) == segment_count : |
198 # parent is the one of the current stack level, stack doesn't change |
98 siblings.append((url, title)) |
199 parent = stack_parent |
|
200 |
|
201 # unravel stack |
|
202 elif indent < stack_indent : |
|
203 while True : |
|
204 # remove current stack level |
|
205 stack.pop(-1) |
|
206 |
|
207 # peek next level |
|
208 stack_indent, stack_parent = stack[-1] |
|
209 |
|
210 # found the level to return to? |
|
211 if stack_indent == indent : |
|
212 # restore prev |
|
213 parent = stack_parent |
|
214 |
|
215 break |
|
216 |
|
217 elif stack_indent < indent : |
|
218 assert False, "Bad un-indent" |
|
219 |
|
220 # add to parent? |
|
221 if parent : |
|
222 item.set_parent(parent) |
|
223 parent.add_child(item) |
|
224 |
|
225 # update prev |
|
226 prev = item |
|
227 |
|
228 # get root |
|
229 assert hasattr(self, 'root'), "No root item found!" |
|
230 |
|
231 def get_page (self, url) : |
|
232 """ |
|
233 Lookup the given page URL, and return the matching PageInfo object, or None, if not found |
|
234 """ |
|
235 |
|
236 # start from root |
|
237 node = self.root |
|
238 |
|
239 # traverse the object tree |
|
240 for segment in url.split('/') : |
|
241 if segment : |
|
242 node = node.get_child(segment) |
|
243 |
|
244 if not node : |
|
245 return None |
99 |
246 |
100 # return |
247 # return |
101 return siblings |
248 return node |
102 |
249 |
103 # global singleton instance |
250 def get_siblings (self, url) : |
104 page_list = PageList() |
251 """ |
105 |
252 Get the list of siblings for the given url, including the given page itself |
|
253 """ |
|
254 |
|
255 # look up the page itself |
|
256 page = self.get_page(url) |
|
257 |
|
258 # specialcase root/unknown node |
|
259 if page and page.parent : |
|
260 return page.parent.children |
|
261 |
|
262 else : |
|
263 return self.root.children |
|
264 |
|
265 def dump (self) : |
|
266 """ |
|
267 Returns a string representation of the tree |
|
268 """ |
|
269 |
|
270 def _print_node (indent, node) : |
|
271 return '\n'.join('%s%s' % (' '*indent, line) for line in [ |
|
272 "%-15s : %s" % (node.name, node.title) |
|
273 ] + [ |
|
274 _print_node(indent + 4, child) for child in node.children |
|
275 ]) |
|
276 |
|
277 return _print_node(0, self.root) |
|
278 |
|
279 # global singleton PageList instance |
|
280 page_tree = PageTree() |
|
281 |
|
282 def load_page_tree () : |
|
283 """ |
|
284 Load the global singleton PageInfo instance |
|
285 """ |
|
286 |
|
287 page_tree._load() |
|
288 |
|
289 # XXX: should inherit from PageInfo |
106 class Page (object) : |
290 class Page (object) : |
107 """ |
291 """ |
108 This object represents the information about our attempt to render some specific page |
292 This object represents the information about our attempt to render some specific page |
109 """ |
293 """ |
110 |
294 |