--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/www.qmsk.net/page_tree.py Sat Feb 07 17:10:06 2009 +0200
@@ -0,0 +1,218 @@
+"""
+ Implements the tree containing pages and their metadata
+"""
+
+from lib import tree_parse
+
+class PageTreeError (Exception) :
+ """
+ Error parsing/loading the page tree
+ """
+
+ pass
+
+class PageInfo (object) :
+ """
+ Contains metainformation about a page
+ """
+
+ def __init__ (self, parent, name, title, children=None) :
+ """
+ Initialize, children defaults to empty list
+ """
+
+ # store
+ self.parent = parent
+ self.name = name
+ self.title = title
+ self.children = children if children else []
+
+ # no url get
+ self._url = None
+
+ def set_parent (self, parent) :
+ """
+ Set a parent where non was set before
+ """
+
+ assert self.parent is None
+
+ self.parent = parent
+
+ def add_child (self, child) :
+ """
+ Add a PageInfo child
+ """
+
+ self.children.append(child)
+
+ def get_child (self, name) :
+ """
+ Look up a child by name, returning None if not found
+ """
+
+ return dict((c.name, c) for c in self.children).get(name)
+
+ def get_ancestry (self) :
+ """
+ Returns a list of this page's parents and the page itself, but not root
+ """
+
+ # collect in reverse order
+ ancestry = []
+
+ # starting from self
+ item = self
+
+ # add all items, but not root
+ while item and item.parent :
+ ancestry.append(item)
+
+ item = item.parent
+
+ # reverse
+ ancestry.reverse()
+
+ # done
+ return ancestry
+
+ @property
+ def url (self) :
+ """
+ Build this page's URL
+ """
+
+ # cached?
+ if self._url :
+ return self._url
+
+ segments = [item.name for item in self.get_ancestry()]
+
+ # add empty segment if dir
+ if self.children :
+ segments.append('')
+
+ # join
+ url = '/'.join(segments)
+
+ # cache
+ self._url = url
+
+ # done
+ return url
+
+class PageTree (object) :
+ """
+ The tree of pages, rooted at .root.
+
+ Use load_page_tree to initialize the global page_tree instance, and then use that
+ """
+
+ def __init__ (self, path) :
+ """
+ Loads the PageTree root from the given file
+ """
+
+ # store
+ self.path = path
+
+ # load
+ self._load(path)
+
+ def _load (self, path) :
+ """
+ Processes the lines in the given file
+ """
+
+ # parse tree
+ tree = tree_parse.parse(path, ':')
+
+ if not tree :
+ raise PageTreeError("No root node found")
+
+ def _create_node (parent, item) :
+ """
+ Creates and returns a PageInfo from the given parent node and (line_number, line, children) tuple item
+ """
+
+ # unpack
+ line_number, line, children = item
+
+ # parse line
+ url = title = None
+
+ try :
+ url, title = line.split(':')
+
+ except :
+ raise PageTreeError("Invalid line: %s:%d: %r" % (path, line_number, line))
+
+ # remove whitespace
+ url = url.strip()
+ title = title.strip()
+
+ # create PageInfo
+ node = PageInfo(parent, url, title)
+
+ # set node children
+ node.children = [
+ _create_node(node, child_item) for child_item in children
+ ]
+
+ # return
+ return node
+
+ # translate
+ self.root = _create_node(None, tree)
+
+ # *evil cackle*
+ self.root.children.insert(0, self.root)
+
+ def get_page (self, url) :
+ """
+ Lookup the given page URL, and return the matching PageInfo object, or None, if not found
+ """
+
+ # start from root
+ node = self.root
+
+ # traverse the object tree
+ for segment in url.split('/') :
+ if segment :
+ node = node.get_child(segment)
+
+ if not node :
+ return None
+
+ # return
+ return node
+
+ def get_siblings (self, url) :
+ """
+ Get the list of siblings for the given url, including the given page itself
+ """
+
+ # look up the page itself
+ page = self.get_page(url)
+
+ # specialcase root/unknown node
+ if page and page.parent :
+ return page.parent.children
+
+ else :
+ return self.root.children
+
+ def dump (self) :
+ """
+ Returns a string representation of the tree
+ """
+
+ def _print_node (indent, node) :
+ return '\n'.join('%s%s' % (' '*indent, line) for line in [
+ "%-15s : %s" % (node.name, node.title)
+ ] + [
+ _print_node(indent + 4, child) for child in node.children if child != node
+ ])
+
+ return _print_node(0, self.root)
+