OwlCyberSecurity - MANAGER
Edit File: tables.py
## # Copyright (c) 2009-2017 Apple Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ## from sys import stdout import types class Table(object): """ Class that allows pretty printing ascii tables. The table supports multiline headers and footers, independent column formatting by row, alternative tab-delimited output. """ class ColumnFormat(object): """ Defines the format string, justification and span for a column. """ LEFT_JUSTIFY = 0 RIGHT_JUSTIFY = 1 CENTER_JUSTIFY = 2 def __init__(self, strFormat="%s", justify=LEFT_JUSTIFY, span=1): self.format = strFormat self.justify = justify self.span = span def __init__(self, table=None): self.headers = [] self.headerColumnFormats = [] self.rows = [] self.footers = [] self.footerColumnFormats = [] self.columnCount = 0 self.defaultColumnFormats = [] self.columnFormatsByRow = {} if table: self.setData(table) def setData(self, table): self.hasTitles = True self.headers.append(table[0]) self.rows = table[1:] self._getMaxColumnCount() def setDefaultColumnFormats(self, columnFormats): self.defaultColumnFormats = columnFormats def addDefaultColumnFormat(self, columnFormat): self.defaultColumnFormats.append(columnFormat) def setHeaders(self, rows, columnFormats=None): self.headers = rows self.headerColumnFormats = columnFormats if columnFormats else [None, ] * len(self.headers) self._getMaxColumnCount() def addHeader(self, row, columnFormats=None): self.headers.append(row) self.headerColumnFormats.append(columnFormats) self._getMaxColumnCount() def addHeaderDivider(self, skipColumns=()): self.headers.append((None, skipColumns,)) self.headerColumnFormats.append(None) def setFooters(self, row, columnFormats=None): self.footers = row self.footerColumnFormats = columnFormats if columnFormats else [None, ] * len(self.footers) self._getMaxColumnCount() def addFooter(self, row, columnFormats=None): self.footers.append(row) self.footerColumnFormats.append(columnFormats) self._getMaxColumnCount() def addRow(self, row=None, columnFormats=None): self.rows.append(row) if columnFormats: self.columnFormatsByRow[len(self.rows) - 1] = columnFormats self._getMaxColumnCount() def addDivider(self, skipColumns=()): self.rows.append((None, skipColumns,)) def printTable(self, os=stdout): maxWidths = self._getMaxWidths() self.printDivider(os, maxWidths, False) if self.headers: for header, format in zip(self.headers, self.headerColumnFormats): self.printRow(os, header, self._getHeaderColumnFormat(format), maxWidths) self.printDivider(os, maxWidths) for ctr, row in enumerate(self.rows): self.printRow(os, row, self._getColumnFormatForRow(ctr), maxWidths) if self.footers: self.printDivider(os, maxWidths, double=True) for footer, format in zip(self.footers, self.footerColumnFormats): self.printRow(os, footer, self._getFooterColumnFormat(format), maxWidths) self.printDivider(os, maxWidths, False) def printRow(self, os, row, format, maxWidths): if row is None or type(row) is tuple and row[0] is None: self.printDivider(os, maxWidths, skipColumns=row[1] if type(row) is tuple else ()) else: if len(row) != len(maxWidths): row = list(row) row.extend([""] * (len(maxWidths) - len(row))) t = "|" ctr = 0 while ctr < len(row): startCtr = ctr maxWidth = 0 for _ignore_span in xrange(format[startCtr].span if format else 1): maxWidth += maxWidths[ctr] ctr += 1 maxWidth += 3 * ((format[startCtr].span - 1) if format else 0) text = self._columnText(row, startCtr, format, width=maxWidth) t += " " + text + " |" t += "\n" os.write(t) def printDivider(self, os, maxWidths, intermediate=True, double=False, skipColumns=()): t = "|" if intermediate else "+" for widthctr, width in enumerate(maxWidths): if widthctr in skipColumns: c = " " else: c = "=" if double else "-" t += c * (width + 2) t += "+" if widthctr < len(maxWidths) - 1 else ("|" if intermediate else "+") t += "\n" os.write(t) def printTabDelimitedData(self, os=stdout, footer=True): if self.headers: titles = [""] * len(self.headers[0]) for row, header in enumerate(self.headers): for col, item in enumerate(header): titles[col] += (" " if row and item else "") + item self.printTabDelimitedRow(os, titles, self._getHeaderColumnFormat(self.headerColumnFormats[0])) for ctr, row in enumerate(self.rows): self.printTabDelimitedRow(os, row, self._getColumnFormatForRow(ctr)) if self.footers and footer: for footer in self.footers: self.printTabDelimitedRow(os, footer, self._getFooterColumnFormat(self.footerColumnFormats[0])) def printTabDelimitedRow(self, os, row, format): if row is None: row = [""] * self.columnCount if len(row) != self.columnCount: row = list(row) row.extend([""] * (self.columnCount - len(row))) textItems = [self._columnText(row, ctr, format) for ctr in xrange((len(row)))] os.write("\t".join(textItems) + "\n") def _getMaxColumnCount(self): self.columnCount = 0 if self.headers: for header in self.headers: self.columnCount = max(self.columnCount, len(header) if header else 0) for row in self.rows: self.columnCount = max(self.columnCount, len(row) if row else 0) if self.footers: for footer in self.footers: self.columnCount = max(self.columnCount, len(footer) if footer else 0) def _getMaxWidths(self): maxWidths = [0] * self.columnCount if self.headers: for header, format in zip(self.headers, self.headerColumnFormats): self._updateMaxWidthsFromRow(header, self._getHeaderColumnFormat(format), maxWidths) for ctr, row in enumerate(self.rows): self._updateMaxWidthsFromRow(row, self._getColumnFormatForRow(ctr), maxWidths) if self.footers: for footer, format in zip(self.footers, self.footerColumnFormats): self._updateMaxWidthsFromRow(footer, self._getFooterColumnFormat(format), maxWidths) return maxWidths def _updateMaxWidthsFromRow(self, row, format, maxWidths): if row and (type(row) is not tuple or row[0] is not None): ctr = 0 while ctr < len(row): text = self._columnText(row, ctr, format) startCtr = ctr for _ignore_span in xrange(format[startCtr].span if format else 1): maxWidths[ctr] = max(maxWidths[ctr], len(text) / (format[startCtr].span if format else 1)) ctr += 1 def _getHeaderColumnFormat(self, format): if format: return format else: justify = Table.ColumnFormat.CENTER_JUSTIFY if len(self.headers) == 1 else Table.ColumnFormat.LEFT_JUSTIFY return [Table.ColumnFormat(justify=justify)] * self.columnCount def _getFooterColumnFormat(self, format): if format: return format else: return self.defaultColumnFormats def _getColumnFormatForRow(self, ctr): if ctr in self.columnFormatsByRow: return self.columnFormatsByRow[ctr] else: return self.defaultColumnFormats def _columnText(self, row, column, format, width=0): if row is None or column >= len(row): return "" colData = row[column] if colData is None: colData = "" columnFormat = format[column] if format and column < len(format) else Table.ColumnFormat() if type(colData) in types.StringTypes: text = colData else: text = columnFormat.format % colData if width: if columnFormat.justify == Table.ColumnFormat.LEFT_JUSTIFY: text = text.ljust(width) elif columnFormat.justify == Table.ColumnFormat.RIGHT_JUSTIFY: text = text.rjust(width) elif columnFormat.justify == Table.ColumnFormat.CENTER_JUSTIFY: text = text.center(width) return text