summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAurelien Campeas <aurelien.campeas@logilab.fr>2009-08-20 12:57:04 +0200
committerAurelien Campeas <aurelien.campeas@logilab.fr>2009-08-20 12:57:04 +0200
commit2152e5f42897a8f6de6475c49e671f802dc71df8 (patch)
treefcc2dc937fbac9a7092b7ffcd753bbcdafc774de
parent1b31b5f655b08f3ba2112b3b4575ec92706498d9 (diff)
downloadlogilab-common-2152e5f42897a8f6de6475c49e671f802dc71df8.tar.gz
[html] resurrect html module with functionnality from cubicweb.ui (actually not used in cw)
-rw-r--r--decorators.py1
-rw-r--r--html.py125
-rw-r--r--test/unittest_html.py59
3 files changed, 184 insertions, 1 deletions
diff --git a/decorators.py b/decorators.py
index d7768a0..f5278cc 100644
--- a/decorators.py
+++ b/decorators.py
@@ -81,7 +81,6 @@ def copy_cache(obj, funcname, cacheobj):
except KeyError:
pass
-
class wproperty(object):
"""Simple descriptor expecting to take a modifier function as first argument
and looking for a _<function name> to retrieve the attribute.
diff --git a/html.py b/html.py
new file mode 100644
index 0000000..5c949ab
--- /dev/null
+++ b/html.py
@@ -0,0 +1,125 @@
+"""render a tree in HTML.
+
+:copyright: 2000-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: General Public License version 2 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+
+def render_HTML_tree(tree, selected_node=None, render_node=None, caption=None):
+ """
+ Generate a pure HTML representation of a tree given as an instance
+ of a logilab.common.tree.Node
+
+ selected_node is the currently selected node (if any) which will
+ have its surrounding <div> have id="selected" (which default
+ to a bold border libe with the default CSS).
+
+ render_node is a function that should take a Node content (Node.id)
+ as parameter and should return a string (what will be displayed
+ in the cell).
+
+ Warning: proper rendering of the generated html code depends on html_tree.css
+ """
+ tree_depth = tree.depth_down()
+ if render_node is None:
+ render_node = str
+
+ # helper function that build a matrix from the tree, like:
+ # +------+-----------+-----------+
+ # | root | child_1_1 | child_2_1 |
+ # | root | child_1_1 | child_2_2 |
+ # | root | child_1_2 | |
+ # | root | child_1_3 | child_2_3 |
+ # | root | child_1_3 | child_2_4 |
+ # +------+-----------+-----------+
+ # from:
+ # root -+- child_1_1 -+- child_2_1
+ # | |
+ # | +- child_2_2
+ # +- child_1_2
+ # |
+ # +- child1_3 -+- child_2_3
+ # |
+ # +- child_2_2
+ def build_matrix(path, matrix):
+ if path[-1].is_leaf():
+ matrix.append(path[:])
+ else:
+ for child in path[-1].children:
+ build_matrix(path[:] + [child], matrix)
+
+ matrix = []
+ build_matrix([tree], matrix)
+
+ # make all lines in the matrix have the same number of columns
+ for line in matrix:
+ line.extend([None]*(tree_depth-len(line)))
+ for i in range(len(matrix)-1, 0, -1):
+ prev_line, line = matrix[i-1:i+1]
+ for j in range(len(line)):
+ if line[j] == prev_line[j]:
+ line[j] = None
+
+ # We build the matrix of link types (between 2 cells on a line of the matrix)
+ # link types are :
+ link_types = {(True, True, True ): 1, # T
+ (False, False, True ): 2, # |
+ (False, True, True ): 3, # + (actually, vert. bar with horiz. bar on the right)
+ (False, True, False): 4, # L
+ (True, True, False): 5, # -
+ }
+ links = []
+ for i, line in enumerate(matrix):
+ links.append([])
+ for j in range(tree_depth-1):
+ cell_11 = line[j] is not None
+ cell_12 = line[j+1] is not None
+ cell_21 = line[j+1] is not None and line[j+1].next_sibling() is not None
+ link_type = link_types.get((cell_11, cell_12, cell_21), 0)
+ if link_type == 0 and i > 0 and links[i-1][j] in (1, 2, 3):
+ link_type = 2
+ links[-1].append(link_type)
+
+
+ # We can now generate the HTML code for the <table>
+ s = u'<table class="tree">\n'
+ if caption:
+ s += '<caption>%s</caption>\n' % caption
+
+ for i, link_line in enumerate(links):
+ line = matrix[i]
+
+ s += '<tr>'
+ for j, link_cell in enumerate(link_line):
+ cell = line[j]
+ if cell:
+ if cell.id == selected_node:
+ s += '<td class="tree_cell" rowspan="2"><div id="selected" class="tree_cell">%s</div></td>' % (render_node(cell.id))
+ else:
+ s += '<td class="tree_cell" rowspan="2"><div class="tree_cell">%s</div></td>' % (render_node(cell.id))
+ else:
+ s += '<td rowspan="2">&nbsp;</td>'
+ s += '<td class="tree_cell_%d_1">&nbsp;</td>' % link_cell
+ s += '<td class="tree_cell_%d_2">&nbsp;</td>' % link_cell
+
+ cell = line[-1]
+ if cell:
+ if cell.id == selected_node:
+ s += '<td class="tree_cell" rowspan="2"><div id="selected" class="tree_cell">%s</div></td>' % (render_node(cell.id))
+ else:
+ s += '<td class="tree_cell" rowspan="2"><div class="tree_cell">%s</div></td>' % (render_node(cell.id))
+ else:
+ s += '<td rowspan="2">&nbsp;</td>'
+
+ s += '</tr>\n'
+ if link_line:
+ s += '<tr>'
+ for j, link_cell in enumerate(link_line):
+ s += '<td class="tree_cell_%d_3">&nbsp;</td>' % link_cell
+ s += '<td class="tree_cell_%d_4">&nbsp;</td>' % link_cell
+ s += '</tr>\n'
+
+ s += '</table>'
+ return s
diff --git a/test/unittest_html.py b/test/unittest_html.py
new file mode 100644
index 0000000..927425f
--- /dev/null
+++ b/test/unittest_html.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+"""unittests for logilab.common.html
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+
+__docformat__ = "restructuredtext en"
+
+from logilab.common.testlib import TestCase, unittest_main
+from logilab.common.tree import Node
+
+from logilab.common import html
+
+tree = ('root', (
+ ('child_1_1', (
+ ('child_2_1', ()), ('child_2_2', (
+ ('child_3_1', ()),
+ ('child_3_2', ()),
+ ('child_3_3', ()),
+ )))),
+ ('child_1_2', (('child_2_3', ()),))))
+
+generated_html = """\
+<table class="tree">
+<tr><td class="tree_cell" rowspan="2"><div class="tree_cell">root</div></td><td class="tree_cell_1_1">&nbsp;</td><td class="tree_cell_1_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_1_1</div></td><td class="tree_cell_1_1">&nbsp;</td><td class="tree_cell_1_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_2_1</div></td><td class="tree_cell_0_1">&nbsp;</td><td class="tree_cell_0_2">&nbsp;</td><td rowspan="2">&nbsp;</td></tr>
+<tr><td class="tree_cell_1_3">&nbsp;</td><td class="tree_cell_1_4">&nbsp;</td><td class="tree_cell_1_3">&nbsp;</td><td class="tree_cell_1_4">&nbsp;</td><td class="tree_cell_0_3">&nbsp;</td><td class="tree_cell_0_4">&nbsp;</td></tr>
+<tr><td rowspan="2">&nbsp;</td><td class="tree_cell_2_1">&nbsp;</td><td class="tree_cell_2_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_4_1">&nbsp;</td><td class="tree_cell_4_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div id="selected" class="tree_cell">child_2_2</div></td><td class="tree_cell_1_1">&nbsp;</td><td class="tree_cell_1_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_1</div></td></tr>
+<tr><td class="tree_cell_2_3">&nbsp;</td><td class="tree_cell_2_4">&nbsp;</td><td class="tree_cell_4_3">&nbsp;</td><td class="tree_cell_4_4">&nbsp;</td><td class="tree_cell_1_3">&nbsp;</td><td class="tree_cell_1_4">&nbsp;</td></tr>
+<tr><td rowspan="2">&nbsp;</td><td class="tree_cell_2_1">&nbsp;</td><td class="tree_cell_2_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_0_1">&nbsp;</td><td class="tree_cell_0_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_3_1">&nbsp;</td><td class="tree_cell_3_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_2</div></td></tr>
+<tr><td class="tree_cell_2_3">&nbsp;</td><td class="tree_cell_2_4">&nbsp;</td><td class="tree_cell_0_3">&nbsp;</td><td class="tree_cell_0_4">&nbsp;</td><td class="tree_cell_3_3">&nbsp;</td><td class="tree_cell_3_4">&nbsp;</td></tr>
+<tr><td rowspan="2">&nbsp;</td><td class="tree_cell_2_1">&nbsp;</td><td class="tree_cell_2_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_0_1">&nbsp;</td><td class="tree_cell_0_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_4_1">&nbsp;</td><td class="tree_cell_4_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_3</div></td></tr>
+<tr><td class="tree_cell_2_3">&nbsp;</td><td class="tree_cell_2_4">&nbsp;</td><td class="tree_cell_0_3">&nbsp;</td><td class="tree_cell_0_4">&nbsp;</td><td class="tree_cell_4_3">&nbsp;</td><td class="tree_cell_4_4">&nbsp;</td></tr>
+<tr><td rowspan="2">&nbsp;</td><td class="tree_cell_4_1">&nbsp;</td><td class="tree_cell_4_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_1_2</div></td><td class="tree_cell_5_1">&nbsp;</td><td class="tree_cell_5_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_2_3</div></td><td class="tree_cell_0_1">&nbsp;</td><td class="tree_cell_0_2">&nbsp;</td><td rowspan="2">&nbsp;</td></tr>
+<tr><td class="tree_cell_4_3">&nbsp;</td><td class="tree_cell_4_4">&nbsp;</td><td class="tree_cell_5_3">&nbsp;</td><td class="tree_cell_5_4">&nbsp;</td><td class="tree_cell_0_3">&nbsp;</td><td class="tree_cell_0_4">&nbsp;</td></tr>
+</table>\
+"""
+
+def make_tree(tupletree):
+ n = Node(tupletree[0])
+ for child in tupletree[1]:
+ n.append(make_tree(child))
+ return n
+
+class UIlibHTMLGenerationTC(TestCase):
+ """ a basic tree node, caracterised by an id"""
+ def setUp(self):
+ """ called before each test from this class """
+ self.o = make_tree(tree)
+
+ def test_generated_html(self):
+ s = html.render_HTML_tree(self.o, selected_node="child_2_2")
+ self.assertTextEqual(s, generated_html)
+
+
+if __name__ == '__main__':
+ unittest_main()