| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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 __future__ import unicode_literals |
| from __future__ import division |
| from __future__ import absolute_import |
| from __future__ import print_function |
| |
| from time import strftime, gmtime |
| from qpid_dispatch_internal.compat import UNICODE |
| |
| |
| def YN(val): |
| if val: |
| return 'Y' |
| return 'N' |
| |
| |
| def Commas(value): |
| sval = str(value) |
| result = "" |
| while True: |
| if len(sval) == 0: |
| return result |
| left = sval[:-3] |
| right = sval[-3:] |
| result = right + result |
| if len(left) > 0: |
| result = ',' + result |
| sval = left |
| |
| |
| def TimeLong(value): |
| day = value // (24 * 3600) |
| time = value % (24 * 3600) |
| hour = time // 3600 |
| time %= 3600 |
| minutes = time // 60 |
| time %= 60 |
| seconds = time |
| return "%03d:%02d:%02d:%02d" % (day, hour, minutes, seconds) |
| |
| |
| def TimeShort(value): |
| return strftime("%X", gmtime(value / 1000000000)) |
| |
| |
| def NumKMG(value, base=1000): |
| """ |
| Format large numbers in a human readable summary |
| """ |
| # IEEE 1541 numeric suffix definitions: |
| SUFFIX = {1024: ('KiB', 'MiB', 'GiB', 'TiB', 'PiB'), |
| 1000: ('k', 'm', 'g', 't', 'p')} |
| |
| def _numCell(fp, suffix): |
| # adjust the precision based on the size |
| if fp < 10.0: |
| return "%.2f %s" % (fp, suffix) |
| if fp < 100.0: |
| return "%.1f %s" % (fp, suffix) |
| return "%.0f %s" % (fp, suffix) |
| |
| if value < base: |
| return "%d" % value |
| |
| # round down to a power of base: |
| sx = SUFFIX[base] |
| for i in range(len(sx)): |
| value /= float(base) |
| if value < base: |
| return _numCell(value, sx[i]) |
| return _numCell(value, sx[-1]) |
| |
| |
| class Header: |
| """ """ |
| NONE = 1 |
| KMG = 2 # 1000 based units |
| YN = 3 |
| Y = 4 |
| TIME_LONG = 5 |
| TIME_SHORT = 6 |
| DURATION = 7 |
| COMMAS = 8 |
| # This is a plain number, no formatting |
| PLAIN_NUM = 9 |
| KiMiGi = 10 # 1024 based units |
| |
| def __init__(self, text, format=NONE): |
| self.text = text |
| self.format = format |
| |
| def __repr__(self): |
| return self.text |
| |
| def __str__(self): |
| return self.text |
| |
| def formatted(self, value): |
| try: |
| if value is None: |
| return '' |
| if self.format == Header.NONE: |
| return value |
| if self.format == Header.PLAIN_NUM: |
| return PlainNum(value) |
| if self.format == Header.KMG: |
| return NumKMG(value) |
| if self.format == Header.KiMiGi: |
| return NumKMG(value, base=1024) |
| if self.format == Header.YN: |
| if value: |
| return 'Y' |
| return 'N' |
| if self.format == Header.Y: |
| if value: |
| return 'Y' |
| return '' |
| if self.format == Header.TIME_LONG: |
| return TimeLong(value) |
| if self.format == Header.TIME_SHORT: |
| return TimeShort(value) |
| if self.format == Header.DURATION: |
| if value < 0: |
| value = 0 |
| sec = value / 1000000000 |
| min = sec / 60 |
| hour = min / 60 |
| day = hour / 24 |
| result = "" |
| if day > 0: |
| result = "%dd " % day |
| if hour > 0 or result != "": |
| result += "%dh " % (hour % 24) |
| if min > 0 or result != "": |
| result += "%dm " % (min % 60) |
| result += "%ds" % (sec % 60) |
| return result |
| if self.format == Header.COMMAS: |
| return Commas(value) |
| except: |
| return "?" |
| |
| |
| def PlainNum(value): |
| try: |
| ret_val = "%d" % value |
| return ret_val |
| except: |
| return "%s" % value |
| |
| |
| class BodyFormat: |
| """ |
| Display body format chooses between: |
| CLASSIC - original variable-width, unquoted, text delimited by white space |
| CSV - quoted text delimited by commas |
| """ |
| CLASSIC = 1 |
| CSV = 2 |
| |
| |
| class CSV_CONFIG: |
| """ """ |
| SEPERATOR = u',' |
| STRING_QUOTE = u'"' |
| |
| |
| class Display: |
| """ Display formatting """ |
| |
| def __init__(self, spacing=2, prefix=" ", bodyFormat=BodyFormat.CLASSIC): |
| self.tableSpacing = spacing |
| self.tablePrefix = prefix |
| self.timestampFormat = "%X" |
| if bodyFormat == BodyFormat.CLASSIC: |
| self.printTable = self.table |
| elif bodyFormat == BodyFormat.CSV: |
| self.printTable = self.tableCsv |
| else: |
| raise Exception("Table body format must be CLASSIC or CSV.") |
| |
| def formattedTable(self, title, heads, rows): |
| fRows = [] |
| for row in rows: |
| fRow = [] |
| col = 0 |
| for cell in row: |
| fRow.append(heads[col].formatted(cell)) |
| col += 1 |
| fRows.append(fRow) |
| headtext = [] |
| for head in heads: |
| headtext.append(head.text) |
| self.printTable(title, headtext, fRows) |
| |
| def table(self, title, heads, rows): |
| """ Print a table with autosized columns """ |
| |
| # Pad the rows to the number of heads |
| for row in rows: |
| diff = len(heads) - len(row) |
| for idx in range(diff): |
| row.append("") |
| |
| print("%s" % title) |
| if len(rows) == 0: |
| return |
| colWidth = [] |
| col = 0 |
| line = self.tablePrefix |
| for head in heads: |
| width = len(head) |
| for row in rows: |
| text = UNICODE(row[col]) |
| cellWidth = len(text) |
| if cellWidth > width: |
| width = cellWidth |
| colWidth.append(width + self.tableSpacing) |
| line = line + head |
| if col < len(heads) - 1: |
| for i in range(colWidth[col] - len(head)): |
| line = line + " " |
| col = col + 1 |
| print(line) |
| line = self.tablePrefix |
| for width in colWidth: |
| for i in range(width): |
| line = line + "=" |
| print(line) |
| |
| for row in rows: |
| line = self.tablePrefix |
| col = 0 |
| for width in colWidth: |
| text = UNICODE(row[col]) |
| line = line + text |
| if col < len(heads) - 1: |
| for i in range(width - len(text)): |
| line = line + " " |
| col = col + 1 |
| print(line) |
| |
| def tableCsv(self, title, heads, rows): |
| """ |
| Print a table with CSV format. |
| """ |
| |
| def csvEscape(text): |
| """ |
| Given a unicode text field, return the quoted CSV format for it |
| :param text: a header field or a table row field |
| :return: |
| """ |
| if len(text) == 0: |
| return "" |
| else: |
| text = text.replace(CSV_CONFIG.STRING_QUOTE, CSV_CONFIG.STRING_QUOTE * 2) |
| return CSV_CONFIG.STRING_QUOTE + text + CSV_CONFIG.STRING_QUOTE |
| |
| print("%s" % title) |
| if len(rows) == 0: |
| return |
| |
| print(','.join([csvEscape(UNICODE(head)) for head in heads])) |
| for row in rows: |
| print(','.join([csvEscape(UNICODE(item)) for item in row])) |
| |
| def do_setTimeFormat(self, fmt): |
| """ Select timestamp format """ |
| if fmt == "long": |
| self.timestampFormat = "%c" |
| elif fmt == "short": |
| self.timestampFormat = "%X" |
| |
| def timestamp(self, nsec): |
| """ Format a nanosecond-since-the-epoch timestamp for printing """ |
| return strftime(self.timestampFormat, gmtime(nsec / 1000000000)) |
| |
| def duration(self, nsec): |
| if nsec < 0: |
| nsec = 0 |
| sec = nsec / 1000000000 |
| min = sec / 60 |
| hour = min / 60 |
| day = hour / 24 |
| result = "" |
| if day > 0: |
| result = "%dd " % day |
| if hour > 0 or result != "": |
| result += "%dh " % (hour % 24) |
| if min > 0 or result != "": |
| result += "%dm " % (min % 60) |
| result += "%ds" % (sec % 60) |
| return result |
| |
| |
| class Sortable(object): |
| """ """ |
| |
| def __init__(self, row, sortIndex): |
| self.row = row |
| self.sortIndex = sortIndex |
| if sortIndex >= len(row): |
| raise Exception("sort index exceeds row boundary") |
| |
| def __lt__(self, other): |
| return self.row[self.sortIndex] < other.row[self.sortIndex] |
| |
| def getRow(self): |
| return self.row |
| |
| |
| class Sorter: |
| """ """ |
| |
| def __init__(self, heads, rows, sortCol, limit=0, inc=True): |
| col = 0 |
| for head in heads: |
| if head.text == sortCol: |
| break |
| col += 1 |
| if col == len(heads): |
| raise Exception("sortCol '%s', not found in headers" % sortCol) |
| |
| list = [] |
| for row in rows: |
| list.append(Sortable(row, col)) |
| list.sort() |
| if not inc: |
| list.reverse() |
| count = 0 |
| self.sorted = [] |
| for row in list: |
| self.sorted.append(row.getRow()) |
| count += 1 |
| if count == limit: |
| break |
| |
| def getSorted(self): |
| return self.sorted |