Module: check_mk
Branch: master
Commit: d3de9e474c8eb845eb3da0fc7ee60b50abf88148
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=d3de9e474c8eb8…
Author: Roland Halbig <rh(a)mathias-kettner.de>
Date: Thu Jan 5 15:47:48 2017 +0100
Refactoring table.py: Isolated sort and filter functions.
Change-Id: I16762356db788386c2f1abdd0f67d4ca1971162d
---
tests/web/test_table.py | 141 ++++++++++++++++++++++++++++++++++++++++++++++++
web/htdocs/table.py | 87 +++++++++++++++++-------------
2 files changed, 192 insertions(+), 36 deletions(-)
diff --git a/tests/web/test_table.py b/tests/web/test_table.py
new file mode 100644
index 0000000..7ce30e6
--- /dev/null
+++ b/tests/web/test_table.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python
+# call using
+# > py.test -s -k test_html_generator.py
+
+# enable imports from web directory
+from testlib import cmk_path
+import sys, os
+sys.path.insert(0, "%s/web/htdocs" % cmk_path())
+
+# external imports
+import re
+from bs4 import BeautifulSoup as bs
+
+# internal imports
+from htmllib import html
+import __builtin__
+from htmllib import HTMLGenerator, HTMLCheck_MK
+from tools import compare_html , gentest, compare_and_empty
+from classes import DeprecatedRenderer
+
+
+try:
+ from config.user import load_file, save_file
+except:
+ load_file = lambda x, y: {}
+ save_file = lambda x, y: None
+
+import table
+
+import traceback
+
+class TableTest(html):
+
+ written_text = ''
+ tag_counter = 0
+
+ def lowlevel_write(self, text):
+
+ if re.match(r'.*\.close_\w+[(][)]',
'\n'.join(traceback.format_stack()), re.DOTALL):
+ self.tag_counter -= 1 if self.tag_counter > 0 else 0
+ self.written_text += " " * 4 * self.tag_counter + text
+ elif re.match(r'.*\.open_\w+[(]',
'\n'.join(traceback.format_stack()), re.DOTALL):
+ self.written_text += " " * 4 * self.tag_counter + text
+ self.tag_counter += 1
+ else:
+ self.written_text += " " * 4 * self.tag_counter + text +
''
+
+
+def read_out_table(text):
+ # Get the contents of the table as a list of lists
+ data = []
+ for row in bs(text, 'html5lib').findAll('tr'):
+ columns = row.findAll('th')
+ if not columns:
+ columns = row.findAll('td')
+ data.append([re.sub(r'\s', '',
re.sub(r'<[^<]*>', '', cell.text)) for cell in columns])
+ return data
+
+
+def read_out_csv(text, separator):
+ # Get the contents of the table as a list of lists
+ data = []
+ for row in text.split('\n'):
+ columns = row.split(separator)
+ data.append([re.sub(r'\s', '',
re.sub(r'<[^<]*>', '', cell)) for cell in columns])
+ data = [row for row in data if not all(cell == '' for cell in row)]
+ return data
+
+
+def table_test_cubical(sortable, searchable, limit, output_format):
+
+ html = TableTest()
+ __builtin__.html = html
+
+ table_id = 0
+ title = " TEST "
+ separator = ';'
+
+ html.add_var('_%s_sort' % table_id, "1,0")
+
+ rows = [ (i, i**3) for i in range(10) ]
+ header = ["Number", "Cubical"]
+
+ table.begin(table_id = table_id,
+ title = title,
+ sortable = sortable,
+ searchable = searchable,
+ limit = limit,
+ output_format = output_format)
+
+ for row in rows:
+ table.row()
+ for i in range(len(header)):
+ table.cell(_(header[i]), row[i])
+ table.end()
+
+ # write to file: This is part of the test! Look at the generated HTML!
+ filename = "./web/testtable_%s_%s_%s.html" % ("sortable" *
sortable, "searchable" * searchable, limit)
+ text = html.written_text
+ with open(filename, "w") as html_file:
+ html_file.write(text)
+
+ # Data assertions
+ assert output_format in ['html', 'csv'], 'Fetch is not yet
implemented'
+ if output_format == 'html':
+ data = read_out_table(text)
+ assert data.pop(0) == header
+ elif output_format == 'csv':
+ data = read_out_csv(text, separator)
+ assert data.pop(0) == header, header
+ else:
+ assert False, 'Not yet implemented!'
+
+ data = [ tuple(map(int, row)) for row in data ]
+ if limit is None:
+ limit = len(rows)
+
+ assert len(data) == limit
+ assert data == rows[:limit]
+
+
+def test_table():
+ table_test_cubical(False, False, None, 'html')
+
+
+def test_limit():
+ table_test_cubical(False, False, 2, 'html')
+
+
+def test_sortable():
+ table_test_cubical(True, False, None, 'html')
+
+
+def test_searchable():
+ table_test_cubical(False, True, None, 'html')
+
+
+def test_csv():
+ table_test_cubical(False, False, None, 'csv')
+
+
diff --git a/web/htdocs/table.py b/web/htdocs/table.py
index 4615b6b..b69e26b 100644
--- a/web/htdocs/table.py
+++ b/web/htdocs/table.py
@@ -128,6 +128,55 @@ def add_cell(title="", text="", css=None,
help=None, colspan=None, sortable=True
table["headers"].append((title, css, help, sortable))
table["rows"][-1][0].append((htmlcode, css, colspan))
+
+def _filter_rows(rows, search_term):
+ filtered_rows = []
+ for row, css, state, fixed in rows:
+ if state == "header" or fixed:
+ filtered_rows.append((row, css, state, fixed))
+ continue # skip filtering of headers or fixed rows
+
+ for cell_content, css_classes, colspan in row:
+ if search_term in cell_content.lower():
+ filtered_rows.append((row, css, state, fixed))
+ break # skip other cells when matched
+ return filtered_rows
+
+
+def _sort_rows(rows, sort_col, sort_reverse):
+
+ # remove and remind fixed rows, add to separate list
+ fixed_rows = []
+ for index, row in enumerate(rows[:]):
+ if row[3] == True:
+ rows.remove(row)
+ fixed_rows.append((index, row))
+
+ # Then use natural sorting to sort the list. Note: due to a
+ # change in the number of columns of a table in different software
+ # versions the cmp-function might fail. This is because the sorting
+ # column is persisted in a user file. So we ignore exceptions during
+ # sorting. This gives the user the chance to change the sorting and
+ # see the table in the first place.
+ try:
+ rows.sort(cmp=lambda a, b: cmp(num_split(a[0][sort_col][0]),
+ num_split(b[0][sort_col][0])),
+ reverse=sort_reverse==1)
+ except IndexError:
+ pass
+
+ # Now re-add the removed "fixed" rows to the list again
+ if fixed_rows:
+ for index, row in fixed_rows:
+ rows.insert(index, row)
+
+ return rows
+
+
+
+
+
+
def end():
global table
finish_previous()
@@ -194,17 +243,7 @@ def end():
if search_term:
html.set_var('_%s_search' % table_id, search_term)
table_opts['search'] = search_term # persist
- filtered_rows = []
- for row, css, state, fixed in rows:
- if state == "header" or fixed:
- filtered_rows.append((row, css, state, fixed))
- continue # skip filtering of headers or fixed rows
-
- for cell_content, css_classes, colspan in row:
- if search_term in cell_content.lower():
- filtered_rows.append((row, css, state, fixed))
- break # skip other cells when matched
- rows = filtered_rows
+ rows = _filter_rows(rows, search_term)
if html.var('_%s_reset_sorting' % table_id):
html.del_var('_%s_sort' % table_id)
@@ -218,31 +257,7 @@ def end():
html.set_var('_%s_sort' % table_id, sort)
table_opts['sort'] = sort # persist
sort_col, sort_reverse = map(int, sort.split(',', 1))
-
- # remove and remind fixed rows, add to separate list
- fixed_rows = []
- for index, row in enumerate(rows[:]):
- if row[3] == True:
- rows.remove(row)
- fixed_rows.append((index, row))
-
- # Then use natural sorting to sort the list. Note: due to a
- # change in the number of columns of a table in different software
- # versions the cmp-function might fail. This is because the sorting
- # column is persisted in a user file. So we ignore exceptions during
- # sorting. This gives the user the chance to change the sorting and
- # see the table in the first place.
- try:
- rows.sort(cmp=lambda a, b: cmp(num_split(a[0][sort_col][0]),
- num_split(b[0][sort_col][0])),
- reverse=sort_reverse==1)
- except IndexError:
- pass
-
- # Now re-add the removed "fixed" rows to the list again
- if fixed_rows:
- for index, row in fixed_rows:
- rows.insert(index, row)
+ rows = _sort_rows(rows, sort_col, sort_reverse)
num_rows_unlimited = len(rows)
num_cols = len(table["headers"])