terom@16: """ terom@16: Implements the tree containing pages and their metadata terom@16: """ terom@16: terom@16: import tree_parse terom@16: terom@16: # path to file containing the page metadata tree terom@16: PAGE_TREE_FILE = "pages/list" terom@16: terom@16: terom@16: class PageInfo (object) : terom@16: """ terom@16: Contains metainformation about a page terom@16: """ terom@16: terom@16: def __init__ (self, parent, name, title, children=None) : terom@16: """ terom@16: Initialize, children defaults to empty list terom@16: """ terom@16: terom@16: # store terom@16: self.parent = parent terom@16: self.name = name terom@16: self.title = title terom@16: self.children = children if children else [] terom@16: terom@16: # no url get terom@16: self._url = None terom@16: terom@16: def set_parent (self, parent) : terom@16: """ terom@16: Set a parent where non was set before terom@16: """ terom@16: terom@16: assert self.parent is None terom@16: terom@16: self.parent = parent terom@16: terom@16: def add_child (self, child) : terom@16: """ terom@16: Add a PageInfo child terom@16: """ terom@16: terom@16: self.children.append(child) terom@16: terom@16: def get_child (self, name) : terom@16: """ terom@16: Look up a child by name, returning None if not found terom@16: """ terom@16: terom@16: return dict((c.name, c) for c in self.children).get(name) terom@16: terom@16: def get_ancestry (self) : terom@16: """ terom@16: Returns a list of this page's parents and the page itself, but not root terom@16: """ terom@16: terom@16: # collect in reverse order terom@16: ancestry = [] terom@16: terom@16: # starting from self terom@16: item = self terom@16: terom@16: # add all items, but not root terom@16: while item and item.parent : terom@16: ancestry.append(item) terom@16: terom@16: item = item.parent terom@16: terom@16: # reverse terom@16: ancestry.reverse() terom@16: terom@16: # done terom@16: return ancestry terom@16: terom@16: @property terom@16: def url (self) : terom@16: """ terom@16: Build this page's URL terom@16: """ terom@16: terom@16: # cached? terom@16: if self._url : terom@16: return self._url terom@16: terom@16: segments = [item.name for item in self.get_ancestry()] terom@16: terom@16: # add empty segment if dir terom@16: if self.children : terom@16: segments.append('') terom@16: terom@16: # join terom@16: url = '/'.join(segments) terom@16: terom@16: # cache terom@16: self._url = url terom@16: terom@16: # done terom@16: return url terom@16: terom@16: class PageTree (object) : terom@16: """ terom@16: The tree of pages, rooted at .root. terom@16: terom@16: Use load_page_tree to initialize the global page_tree instance, and then use that terom@16: """ terom@16: terom@16: def __init__ (self) : terom@16: """ terom@16: Empty PageList, must call load_page_list to initialize, once terom@16: """ terom@16: terom@16: def _load (self, path) : terom@16: """ terom@16: Processes the lines in the given file terom@16: """ terom@16: terom@16: # parse tree terom@16: tree = tree_parse.parse(path, ':') terom@16: terom@16: def _create_node (parent, item) : terom@16: """ terom@16: Creates and returns a PageInfo from the given parent node and (line_number, line, children) tuple item terom@16: """ terom@16: terom@16: # unpack terom@16: line_number, line, children = item terom@16: terom@16: # parse line terom@16: url, title = line.split(':') terom@16: terom@16: # remove whitespace terom@16: url = url.strip() terom@16: title = title.strip() terom@16: terom@16: # create PageInfo terom@16: node = PageInfo(parent, url, title) terom@16: terom@16: # set node children terom@16: node.children = [ terom@16: _create_node(node, child_item) for child_item in children terom@16: ] terom@16: terom@16: # return terom@16: return node terom@16: terom@16: # translate terom@16: self.root = _create_node(None, tree) terom@16: terom@16: # *evil cackle* terom@16: self.root.children.insert(0, self.root) terom@16: terom@16: def get_page (self, url) : terom@16: """ terom@16: Lookup the given page URL, and return the matching PageInfo object, or None, if not found terom@16: """ terom@16: terom@16: # start from root terom@16: node = self.root terom@16: terom@16: # traverse the object tree terom@16: for segment in url.split('/') : terom@16: if segment : terom@16: node = node.get_child(segment) terom@16: terom@16: if not node : terom@16: return None terom@16: terom@16: # return terom@16: return node terom@16: terom@16: def get_siblings (self, url) : terom@16: """ terom@16: Get the list of siblings for the given url, including the given page itself terom@16: """ terom@16: terom@16: # look up the page itself terom@16: page = self.get_page(url) terom@16: terom@16: # specialcase root/unknown node terom@16: if page and page.parent : terom@16: return page.parent.children terom@16: terom@16: else : terom@16: return self.root.children terom@16: terom@16: def dump (self) : terom@16: """ terom@16: Returns a string representation of the tree terom@16: """ terom@16: terom@16: def _print_node (indent, node) : terom@16: return '\n'.join('%s%s' % (' '*indent, line) for line in [ terom@16: "%-15s : %s" % (node.name, node.title) terom@16: ] + [ terom@16: _print_node(indent + 4, child) for child in node.children if child != node terom@16: ]) terom@16: terom@16: return _print_node(0, self.root) terom@16: terom@16: # global singleton PageList instance terom@16: page_tree = PageTree() terom@16: terom@16: def load () : terom@16: """ terom@16: Load the global singleton PageInfo instance terom@16: """ terom@16: terom@16: page_tree._load(PAGE_TREE_FILE) terom@16: