added pli.structdoc experimental module for reference and as a starting point.
[p2pB.git] / pli-structdoc / structure.py
blob7cbc4243dfc557fa222b200a751671fc8c649b23
1 #=======================================================================
3 __version__ = '''0.0.01'''
4 __sub_version__ = '''20080311152519'''
5 __copyright__ = '''(c) Alex A. Naanou 2008'''
8 #-----------------------------------------------------------------------
10 import pli.structdoc.core as core
12 from pli.functional import curry
13 import time
16 #-----------------------------------------------------------------------
18 # XXX use templates and direct HTML representation for rendering...
19 # Tree ---(template)--> HTML --> file
20 # XXX clean things up...
23 #-----------------------------------------------------------------------
24 # specifics...
25 #-------------------------------------------------------NodeContainer---
26 class NodeContainer(core.NodeContainer):
27 '''
28 '''
29 def __init__(self, *nodes):
30 '''
31 '''
32 ##!!! this is not the best wat to go...
33 super(NodeContainer, self).__init__()
34 # add nodes...
35 for n in nodes:
36 if 'title' in n:
37 i = n['title']
38 else:
39 i = self._get_unique_key()
40 self[i] = n
41 def _get_unique_key(self):
42 '''
43 '''
44 key = str(time.time())
45 while key in self.keys():
46 key = str(time.time())
47 return key
50 ##---------------------------------------------NodeContainerWithTitle---
51 class NodeContainerWithTitle(NodeContainer):
52 '''
53 '''
54 def __init__(self, title, *nodes):
55 '''
56 '''
57 super(NodeContainerWithTitle, self).__init__(*nodes)
58 self['title'] = title
61 #-----------------------------------------------------------------------
62 #----------------------------------------------------------------Site---
63 class Site(NodeContainerWithTitle):
64 '''
65 can contain any number of nodes.
66 '''
67 pass
70 #----------------------------------------------------------------Page---
71 class Page(NodeContainerWithTitle):
72 '''
73 can contain any number of nodes.
74 '''
75 pass
78 #-------------------------------------------------------------Section---
79 class Section(NodeContainerWithTitle):
80 '''
81 can contain any number of nodes.
82 '''
83 pass
86 #----------------------------------------------------------------Text---
87 class Text(NodeContainer):
88 '''
89 '''
90 pass
93 #---------------------------------------------------------------Group---
94 class Group(NodeContainer):
95 '''
96 '''
97 pass
100 #----------------------------------------------------------------Link---
101 class Link(NodeContainer):
103 contains two nodes:
104 - text
105 - any node (usually a container)
107 def __init__(self, title, obj):
110 super(Link, self).__init__()
111 self['title'] = title
112 self['obj'] = obj
116 #-----------------------------------------------------------------------
117 # render...
119 # target backends:
120 # - text
121 # - html (view)
122 # - html (edit) -- combined with view
123 # - XXX
125 # * might be a good idea to implement the render as a template...
126 #------------------------------------------------------BaseRSTContext---
127 class BaseRSTContext(core.NodeContext):
130 # render nodes...
131 @property
132 def rst(self):
135 return self._rst_node(1, self._node, cache=set())
136 # render sections...
137 def _rst_str(self, node, cache=None):
140 return node
141 def _rst_unknown(self, level, node, cache=None):
144 return '\n' + ' '*(level-1) + '''%(node_name)s %(title)s:\n%(content)s'''\
145 % dict(
146 title=repr(node.get('title', '')),
147 node_name=node.__class__.__name__,
148 content=('\n' + ' '*(level-1)).join([self._rst_node(level+1, node[n], cache=cache) \
149 for n in node.keys() \
150 if n != 'title' ])) + '\n'
151 def _rst_node(self, level, node, cache=None):
154 t = type(node)
155 # avoid recursion...
156 if node in cache:
157 if 'title' in node:
158 t = node['title']
159 else:
160 # guess some text...
161 t = '.....'
162 return self._rst_node(level, Text(Link(t, node)), cache)
163 else:
164 cache.add(node)
165 # strings...
166 if t in (str, unicode):
167 return self._rst_str(node, cache=cache)
168 return self._rst_unknown(level, node, cache=cache)
171 #----------------------------------------------------------RSTContext---
172 # XXX write a node generator (rst-tree)
173 # XXX needs a selector...
174 ##class RSTContext(core.NodeContext):
175 class RSTContext(BaseRSTContext):
178 # render sections...
179 def _rst_page(self, node, cache=None):
182 return '''%(title)s\n----\n%(content)s'''\
183 % dict(
184 title=node['title'],
185 content='\n'.join([self._rst_node(1, node[n], cache=cache) \
186 for n in node.keys() \
187 if n != 'title']))
188 def _rst_section(self, level, node, cache=None):
191 return '\n' + ' '*(level-1) + '''%(q)s %(title)s %(q)s\n%(content)s'''\
192 % dict(
193 q='='*level,
194 title=node['title'],
195 content=('\n' + ' '*(level-1)).join([self._rst_node(level+1, node[n], cache=cache) \
196 for n in node.keys() \
197 if n != 'title'])) + '\n'
198 def _rst_text(self, level, node, cache=None):
201 ## return '\n' + ' '.join([ self._rst_node(level, node[n]) \
202 return '\n' + ' '*(level-1) + ' '.join([ self._rst_node(level, node[n], cache=cache) \
203 for n in node.keys() ])
204 def _rst_link(self, node, cache=None):
207 return '[[%(title)s|%(url)s|%(alt)s]]' \
208 % dict(
209 title=node['title'],
210 url='mooo',
211 alt='foo')
212 ##!!! make this extend BaseRSTContext._rst_node...
213 def _rst_node(self, level, node, cache=None):
216 t = type(node)
217 # avoid recursion...
218 if node in cache:
219 if 'title' in node:
220 t = node['title']
221 else:
222 # guess some text...
223 t = '.....'
224 return self._rst_node(level, Text(Link(t, node)), cache)
225 else:
226 cache.add(node)
227 # pages...
228 if t is Page:
229 return self._rst_page(node, cache=cache)
230 # links and refs...
231 elif t is Link:
232 return self._rst_link(node, cache=cache)
233 # sections...
234 elif t is Section:
235 return self._rst_section(level, node, cache=cache)
236 # text blocks...
237 elif t is Text:
238 return self._rst_text(level, node, cache=cache)
239 # strings...
240 elif t in (str, unicode):
241 return self._rst_str(node, cache=cache)
242 return self._rst_unknown(level, node, cache=cache)
244 # XXX node generator.... (rst input...)
245 ##!!!
248 #---------------------------------------------------------HTMLContext---
249 # NOTE: this will not create pretty HTML, it was not the goal...
250 # XXX this does not consider titles as nodes... (wrong?)
251 class HTMLContext(core.NodeContext):
254 # templates...
255 # XXX move these to files...
256 _templates = (
257 'html',
258 'page',
259 'section',
260 'text',
261 'emphasized',
262 'link',
263 'unknown',
265 ##!!! this is wrong.... should dynamicly figure this out...
266 _template_dir = './templates/plain/'
268 # props...
269 # XXX move this to someplace more generic...
270 @property
271 def rawhtml(self):
272 cache = set()
273 return self.html_tpl \
274 % dict(
275 title=self.name,
276 content=self._html_node(self, 1, self._node, cache=cache))
277 # XXX make this the default...
278 @property
279 def html(self):
281 render page html
283 cache = set()
284 node = self._get_page()
285 return self.html_tpl \
286 % dict(
287 title=self._node['title'],
288 content=node._html_page(self, 1, node._node, self._node, cache=cache))
289 ##!!! STUB -- need to create a real relative or server absolute URL... (will depend on server)
290 # XXX need to find the shortest path form node to root or to
291 # current page (site-absolute or relative).
292 @property
293 def url(self, target=None):
296 NOTE: if target is omited then return url to self.
298 if target == None:
299 return '/' + '/'.join(self.path)
300 # XXX search....
301 raise NotImplementedError
304 def __init__(self, *p, **n):
307 super(HTMLContext, self).__init__(*p, **n)
308 self._reload_templates()
310 # helpers...
311 def _get_page(self):
313 get the nearest page up in the context...
315 node = self
316 # get parent page...
317 while node.type != 'Page':
318 node = node.parent
319 return node
320 def _reload_templates(self):
323 for tpl in self._templates:
324 setattr(self, tpl + '_tpl', file('%s/%s.tpl' % (self._template_dir, tpl), 'r').read())
325 # processors...
326 def _html_page(self, context, level, page, emphasize=None, cache=None):
329 return self.page_tpl \
330 % dict(
331 title=page['title'],
332 content=''.join([ self._html_node(context, level, page[n], emphasize=emphasize, cache=cache) \
333 for n in page.keys() \
334 if n != 'title' ]))
335 def _html_emphasized(self, context, level, node, cache=None):
338 cache.discard(node)
339 return self.emphasized_tpl \
340 % dict(
341 content=self._html_node(context, level, node, cache=cache))
342 def _html_section(self, context, level, section, emphasize, cache=None):
343 if level > 6:
344 raise ValueError, 'html sections do not support nesting level greater than 6 (got %s)' % level
345 return self.section_tpl \
346 % dict(
347 level=level,
348 title=section['title'],
349 content=''.join([ self._html_node(context, level+1, section[n], emphasize, cache=cache) \
350 for n in section.keys() \
351 if n != 'title' ]))
352 def _html_text(self, context, level, node, cache=None):
355 return self.text_tpl \
356 % ' '.join([ self._html_node(context, level+1, node[n], cache=cache) for n in node.keys() ])
357 def _html_str(self, context, node, cache=None):
360 return node
361 # XXX need to get the referenced object via the context...
362 def _html_link(self, context, node, cache=None):
365 return self.link_tpl \
366 % dict(
367 ## url=node.obj.url,
368 url='mooo',
369 alt='fooo',
370 title=node['title'])
371 def _html_unknown(self, context, level, node, emphasize, cache=None):
374 return self.unknown_tpl \
375 % dict(
376 level=level,
377 node_name=node.__class__.__name__,
378 content=''.join([ self._html_node(context, level, node[n],
379 emphasize=emphasize, cache=cache) \
380 for n in node.keys() ]))
381 def _html_node(self, context, level, node, emphasize=None, cache=None):
382 t = type(node)
383 # avoid recursion...
384 if node in cache:
385 if 'title' in node:
386 t = node['title']
387 else:
388 # guess some text...
389 t = '.....'
390 return self._html_node(context, level, Text(Link(t, node)), emphasize, cache)
391 else:
392 cache.add(node)
393 # links and refs...
394 if node == emphasize:
395 return self._html_emphasized(context, level, node, cache=cache)
396 elif t is Link:
397 return self._html_link(context, node, cache=cache)
398 # sections...
399 elif t is Section:
400 return self._html_section(context, level, node, emphasize, cache=cache)
401 # pages...
402 elif t is Page:
403 return self._html_section(context, level, node, emphasize, cache=cache)
404 # text blocks...
405 elif t is Text:
406 return self._html_text(context, level, node, cache=cache)
407 # strings...
408 elif t in (str, unicode):
409 return self._html_str(context, node, cache=cache)
410 return self._html_unknown(context, level, node, emphasize, cache=cache)
413 #---------------------------------------------------RawRSTHTMLContext---
414 class RawRSTHTMLContext(HTMLContext, BaseRSTContext):
417 pass
420 #------------------------------------------------------RSTHTMLContext---
421 class RSTHTMLContext(HTMLContext, RSTContext):
424 pass
428 #-----------------------------------------------------------------------
429 if __name__ == '__main__':
431 from test_page import root
433 # render and save by path...
434 def render(path, format='html', ext=None):
435 # parse path...
436 path = path.split('/')
437 while '' in path:
438 path.remove('')
439 cur = root
440 # get the node...
441 for n in path:
442 cur = cur[n]
443 if ext == None:
444 ext = format
445 # render and save to file....
446 file('./test/%s.%s' % (cur.name, ext), 'w').write(getattr(cur, format))
449 render('/p')
450 render('/p/first section/')
451 render('p/first section/', format='rawhtml', ext='raw.html')
452 render('p/first section/nested section/')
453 render('p/first section/nested section/another nested section')
455 render('/p', format='rst')
456 render('/p/first section/', format='rst')
457 render('p/first section/nested section/', format='rst')
463 #=======================================================================|# vim:set ts=4 sw=4 nowrap :