# HG changeset patch # User Goffi # Date 1519838919 -3600 # Node ID 984792a451bce022d1e779d7b305a21b76f52f7a # Parent fcf0ae8102b8b763651200d406e116c9f0ef1499 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) diff -r fcf0ae8102b8 -r 984792a451bc frontends/src/jp/common.py --- a/frontends/src/jp/common.py Wed Feb 28 18:28:39 2018 +0100 +++ b/frontends/src/jp/common.py Wed Feb 28 18:28:39 2018 +0100 @@ -25,6 +25,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ head_style = None, show_header=True, show_borders=True, + hide_cols=None, col_sep=u' │ ', top_left=u'┌', top=u'─', @@ -670,6 +680,7 @@ @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 @@ @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 @@ 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 @@ 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 @@ """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_)