jp (common/table): a column can be hidden + fix for empty tables
- avoid crash when table is empty - a column can now be hidden on display, this can be useful if one data is needed (e.g. to change color of an other column with a filter), but we don't want to display it - filters have a new argument with all data of a row (for the same reason as above, one column can be used to change display of other column)
This commit is contained in:
parent
195f30b4d5
commit
976da08f5d
1 changed files with 48 additions and 20 deletions
|
|
@ -25,6 +25,7 @@ from sat.tools.common import uri
|
|||
from sat.tools.common.ansi import ANSI as A
|
||||
from sat.tools import config
|
||||
from ConfigParser import NoSectionError, NoOptionError
|
||||
from collections import namedtuple
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
|
|
@ -509,7 +510,7 @@ class Table(object):
|
|||
|
||||
def __init__(self, host, data, headers=None, filters=None, use_buffer=False):
|
||||
"""
|
||||
@param data(list[list]): table data
|
||||
@param data(iterable[list]): table data
|
||||
all lines must have the same number of columns
|
||||
@param headers(iterable[unicode], None): names/titles of the columns
|
||||
if not None, must have same number of columns as data
|
||||
|
|
@ -530,15 +531,21 @@ class Table(object):
|
|||
self.rows = []
|
||||
|
||||
size = None
|
||||
for line in data:
|
||||
if headers:
|
||||
row_cls = namedtuple('RowData', headers)
|
||||
else:
|
||||
row_cls = tuple
|
||||
|
||||
for row_data in data:
|
||||
new_row = []
|
||||
for idx, value in enumerate(line):
|
||||
row_data_list = list(row_data)
|
||||
for idx, value in enumerate(row_data_list):
|
||||
if filters is not None and filters[idx] is not None:
|
||||
filter_ = filters[idx]
|
||||
if isinstance(filter_, basestring):
|
||||
col_value = filter_.format(value)
|
||||
else:
|
||||
col_value = filter_(value)
|
||||
col_value = filter_(value, row_cls(*row_data_list))
|
||||
# we count size without ANSI code as they will change length of the string
|
||||
# when it's mostly style/color changes.
|
||||
col_size = len(regex.ansiRemove(col_value))
|
||||
|
|
@ -610,18 +617,20 @@ class Table(object):
|
|||
filters = [filters.get(k) for k in keys]
|
||||
return cls(host, (cls.readDictValues(d, keys, defaults) for d in data), headers, filters)
|
||||
|
||||
def _headers(self, head_sep, alignment=u'left', style=None):
|
||||
def _headers(self, head_sep, headers, sizes, alignment=u'left', style=None):
|
||||
"""Render headers
|
||||
|
||||
@param head_sep(unicode): sequence to use as separator
|
||||
@param alignment(unicode): how to align, can be left, center or right
|
||||
@param style(unicode, iterable[unicode], None): ANSI escape sequences to apply
|
||||
@param headers(list[unicode]): headers to show
|
||||
@param sizes(list[int]): sizes of columns
|
||||
"""
|
||||
headers = []
|
||||
rendered_headers = []
|
||||
if isinstance(style, basestring):
|
||||
style = [style]
|
||||
for idx, header in enumerate(self.headers):
|
||||
size = self.sizes[idx]
|
||||
for idx, header in enumerate(headers):
|
||||
size = sizes[idx]
|
||||
if alignment == u'left':
|
||||
rendered = header[:size].ljust(size)
|
||||
elif alignment == u'center':
|
||||
|
|
@ -633,8 +642,8 @@ class Table(object):
|
|||
if style:
|
||||
args = style + [rendered]
|
||||
rendered = A.color(*args)
|
||||
headers.append(rendered)
|
||||
return head_sep.join(headers)
|
||||
rendered_headers.append(rendered)
|
||||
return head_sep.join(rendered_headers)
|
||||
|
||||
def _disp(self, data):
|
||||
"""output data (can be either bufferised or printed)"""
|
||||
|
|
@ -649,6 +658,7 @@ class Table(object):
|
|||
head_style = None,
|
||||
show_header=True,
|
||||
show_borders=True,
|
||||
hide_cols=None,
|
||||
col_sep=u' │ ',
|
||||
top_left=u'┌',
|
||||
top=u'─',
|
||||
|
|
@ -670,6 +680,7 @@ class Table(object):
|
|||
|
||||
@param show_header(bool): True if header need no be shown
|
||||
@param show_borders(bool): True if borders need no be shown
|
||||
@param hide_cols(None, iterable(unicode)): columns which should not be displayed
|
||||
@param head_alignment(unicode): how to align headers, can be left, center or right
|
||||
@param columns_alignment(unicode): how to align columns, can be left, center or right
|
||||
@param col_sep(unicode): separator betweens columns
|
||||
|
|
@ -677,7 +688,24 @@ class Table(object):
|
|||
@param disp(callable, None): method to use to display the table
|
||||
None to use self.host.disp
|
||||
"""
|
||||
if not self.sizes:
|
||||
# the table is empty
|
||||
return
|
||||
col_sep_size = len(regex.ansiRemove(col_sep))
|
||||
|
||||
# if we have columns to hide, we remove them from headers and size
|
||||
if not hide_cols:
|
||||
headers = self.headers
|
||||
sizes = self.sizes
|
||||
else:
|
||||
headers = list(self.headers)
|
||||
sizes = self.sizes[:]
|
||||
ignore_idx = [headers.index(to_hide) for to_hide in hide_cols]
|
||||
for to_hide in hide_cols:
|
||||
hide_idx = headers.index(to_hide)
|
||||
del headers[hide_idx]
|
||||
del sizes[hide_idx]
|
||||
|
||||
if right is None:
|
||||
right = left
|
||||
if top_sep is None:
|
||||
|
|
@ -694,7 +722,7 @@ class Table(object):
|
|||
if show_borders:
|
||||
self._disp(
|
||||
top_left
|
||||
+ top_sep.join([top*size for size in self.sizes])
|
||||
+ top_sep.join([top*size for size in sizes])
|
||||
+ top_right
|
||||
)
|
||||
|
||||
|
|
@ -702,34 +730,36 @@ class Table(object):
|
|||
if show_header:
|
||||
self._disp(
|
||||
left
|
||||
+ self._headers(head_sep, head_alignment, head_style)
|
||||
+ self._headers(head_sep, headers, sizes, head_alignment, head_style)
|
||||
+ right
|
||||
)
|
||||
# header line
|
||||
self._disp(
|
||||
head_line_left
|
||||
+ head_line_sep.join([head_line*size for size in self.sizes])
|
||||
+ head_line_sep.join([head_line*size for size in sizes])
|
||||
+ head_line_right
|
||||
)
|
||||
|
||||
# content
|
||||
if columns_alignment == u'left':
|
||||
alignment = lambda idx, s: ansi_ljust(s, self.sizes[idx])
|
||||
alignment = lambda idx, s: ansi_ljust(s, sizes[idx])
|
||||
elif columns_alignment == u'center':
|
||||
alignment = lambda idx, s: ansi_center(s, self.sizes[idx])
|
||||
alignment = lambda idx, s: ansi_center(s, sizes[idx])
|
||||
elif columns_alignment == u'right':
|
||||
alignment = lambda idx, s: ansi_rjust(s, self.sizes[idx])
|
||||
alignment = lambda idx, s: ansi_rjust(s, sizes[idx])
|
||||
else:
|
||||
raise exceptions.InternalError(u'bad columns alignment argument')
|
||||
|
||||
for row in self.rows:
|
||||
if hide_cols:
|
||||
row = [v for idx,v in enumerate(row) if idx not in ignore_idx]
|
||||
self._disp(left + col_sep.join([alignment(idx,c) for idx,c in enumerate(row)]) + right)
|
||||
|
||||
if show_borders:
|
||||
# bottom border
|
||||
self._disp(
|
||||
bottom_left
|
||||
+ bottom_sep.join([bottom*size for size in self.sizes])
|
||||
+ bottom_sep.join([bottom*size for size in sizes])
|
||||
+ bottom_right
|
||||
)
|
||||
# we return self so string can be used after display (table.display().string)
|
||||
|
|
@ -739,6 +769,4 @@ class Table(object):
|
|||
"""Display table without visible borders"""
|
||||
kwargs_ = {'col_sep':u' ', 'head_line_sep':u' ', 'show_borders':False}
|
||||
kwargs_.update(kwargs)
|
||||
return self.display(self,
|
||||
**kwargs_
|
||||
)
|
||||
return self.display(**kwargs_)
|
||||
|
|
|
|||
Loading…
Reference in a new issue