lib/page.py
changeset 16 4a40718c7b4b
parent 13 178ea40bbc44
child 21 b05979822dee
equal deleted inserted replaced
15:e2fe2baa7910 16:4a40718c7b4b
    10 import http
    10 import http
    11 
    11 
    12 # for TemplatePage
    12 # for TemplatePage
    13 import template
    13 import template
    14 
    14 
       
    15 from page_tree import page_tree
       
    16 
    15 # path to directory containing the page heirarcy
    17 # path to directory containing the page heirarcy
    16 PAGE_DIR = "pages"
    18 PAGE_DIR = "pages"
    17 
    19 
    18 # path to directory containing the list of visible pages
       
    19 PAGE_LIST_FILE = os.path.join(PAGE_DIR, "list")
       
    20 
       
    21 class PageError (http.ResponseError) :
    20 class PageError (http.ResponseError) :
    22     """
    21     """
    23         Error looking up/handling a page
    22         Error looking up/handling a page
    24     """
    23     """
    25 
    24 
    26     pass
    25     pass
    27 
       
    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     def get_ancestry (self) :
       
    71         """
       
    72             Returns a list of this page's parents and the page itself, but not root
       
    73         """
       
    74         
       
    75         # collect in reverse order
       
    76         ancestry = []
       
    77         
       
    78         # starting from self
       
    79         item = self
       
    80         
       
    81         # add all items, but not root
       
    82         while item and item.parent :
       
    83             ancestry.append(item)
       
    84 
       
    85             item = item.parent
       
    86 
       
    87         # reverse
       
    88         ancestry.reverse()
       
    89         
       
    90         # done
       
    91         return ancestry
       
    92 
       
    93     @property
       
    94     def url (self) :
       
    95         """
       
    96             Build this page's URL
       
    97         """
       
    98 
       
    99         # cached?
       
   100         if self._url :
       
   101             return self._url
       
   102 
       
   103         segments = [item.name for item in self.get_ancestry()]
       
   104         
       
   105         # add empty segment if dir
       
   106         if self.children :
       
   107             segments.append('')
       
   108         
       
   109         # join
       
   110         url = '/'.join(segments)
       
   111         
       
   112         # cache
       
   113         self._url = url
       
   114         
       
   115         # done
       
   116         return url
       
   117 
       
   118 class PageTree (object) :
       
   119     """
       
   120         The list of pages
       
   121     """
       
   122 
       
   123     def __init__ (self) :
       
   124         """
       
   125             Empty PageList, must call load_page_list to initialize, once
       
   126         """
       
   127 
       
   128     def _load (self, path=PAGE_LIST_FILE) :
       
   129         """
       
   130             Processes the lines in the given file
       
   131         """
       
   132         
       
   133         # collect the page list
       
   134         pages = []
       
   135         
       
   136         # stack of (indent, PageInfo) items
       
   137         stack = []
       
   138 
       
   139         # the previous item processed, None for first one
       
   140         prev = None
       
   141 
       
   142         for line in open(path, 'rb') :
       
   143             indent = 0
       
   144 
       
   145             # count indent
       
   146             for char in line :
       
   147                 # tabs break things
       
   148                 assert char != '\t'
       
   149                 
       
   150                 # increment up to first non-space char
       
   151                 if char == ' ' :
       
   152                     indent += 1
       
   153 
       
   154                 elif char == ':' :
       
   155                     indent = 0
       
   156                     break
       
   157 
       
   158                 else :
       
   159                     break
       
   160             
       
   161             # strip whitespace
       
   162             line = line.strip()
       
   163 
       
   164             # ignore empty lines
       
   165             if not line :
       
   166                 continue
       
   167 
       
   168             # parse line
       
   169             url, title = line.split(':')
       
   170 
       
   171             # remove whitespace
       
   172             url = url.strip()
       
   173             title = title.strip()
       
   174             
       
   175             # create PageInfo item without parent
       
   176             item = PageInfo(None, url, title)
       
   177 
       
   178             # are we the first item?
       
   179             if not prev :
       
   180                 assert url == '', "Page list must begin with root item"
       
   181                 
       
   182                 # root node does not have a parent
       
   183                 parent = None
       
   184                 
       
   185                 # set root
       
   186                 self.root = item
       
   187 
       
   188                 # tee hee hee
       
   189                 self.root.add_child(self.root)
       
   190 
       
   191                 # initialize stack
       
   192                 stack.append((0, self.root))
       
   193                 
       
   194             else :
       
   195                 # peek stack
       
   196                 stack_indent, stack_parent = stack[-1]
       
   197 
       
   198                 # new indent level?
       
   199                 if indent > stack_indent :
       
   200                     # set parent to previous item, and push new indent level + parent to stack
       
   201                     parent = prev
       
   202 
       
   203                     # push new indent level + its parent
       
   204                     stack.append((indent, parent))
       
   205 
       
   206                 # same indent level as previous
       
   207                 elif indent == stack_indent :
       
   208                     # parent is the one of the current stack level, stack doesn't change
       
   209                     parent = stack_parent
       
   210                 
       
   211                 # unravel stack
       
   212                 elif indent < stack_indent :
       
   213                     while True :
       
   214                         # remove current stack level
       
   215                         stack.pop(-1)
       
   216 
       
   217                         # peek next level
       
   218                         stack_indent, stack_parent = stack[-1]
       
   219                         
       
   220                         # found the level to return to?
       
   221                         if stack_indent == indent :
       
   222                             # restore prev
       
   223                             parent = stack_parent
       
   224 
       
   225                             break
       
   226 
       
   227                         elif stack_indent < indent :
       
   228                             assert False, "Bad un-indent"
       
   229             
       
   230             # add to parent?
       
   231             if parent :
       
   232                 item.set_parent(parent)
       
   233                 parent.add_child(item)
       
   234 
       
   235             # update prev
       
   236             prev = item
       
   237         
       
   238         # get root
       
   239         assert hasattr(self, 'root'), "No root item found!"
       
   240         
       
   241     def get_page (self, url) :
       
   242         """
       
   243             Lookup the given page URL, and return the matching PageInfo object, or None, if not found
       
   244         """
       
   245         
       
   246         # start from root
       
   247         node = self.root
       
   248         
       
   249         # traverse the object tree
       
   250         for segment in url.split('/') :
       
   251             if segment :
       
   252                 node = node.get_child(segment)
       
   253 
       
   254             if not node :
       
   255                 return None
       
   256         
       
   257         # return
       
   258         return node
       
   259     
       
   260     def get_siblings (self, url) :
       
   261         """
       
   262             Get the list of siblings for the given url, including the given page itself
       
   263         """
       
   264         
       
   265         # look up the page itself
       
   266         page = self.get_page(url)
       
   267         
       
   268         # specialcase root/unknown node
       
   269         if page and page.parent :
       
   270             return page.parent.children
       
   271 
       
   272         else :
       
   273             return self.root.children
       
   274     
       
   275     def dump (self) :
       
   276         """
       
   277             Returns a string representation of the tree
       
   278         """
       
   279         
       
   280         def _print_node (indent, node) :
       
   281             return '\n'.join('%s%s' % (' '*indent, line) for line in [
       
   282                 "%-15s : %s" % (node.name, node.title)
       
   283             ] + [
       
   284                 _print_node(indent + 4, child) for child in node.children if child != node
       
   285             ])
       
   286 
       
   287         return _print_node(0, self.root)
       
   288 
       
   289 # global singleton PageList instance
       
   290 page_tree = PageTree()
       
   291 
       
   292 def load_page_tree () :
       
   293     """
       
   294         Load the global singleton PageInfo instance
       
   295     """
       
   296     
       
   297     page_tree._load()
       
   298 
    26 
   299 # XXX: should inherit from PageInfo
    27 # XXX: should inherit from PageInfo
   300 class Page (object) :
    28 class Page (object) :
   301     """
    29     """
   302         This object represents the information about our attempt to render some specific page
    30         This object represents the information about our attempt to render some specific page