diff --git a/README.md b/README.md index 7e8f7bd..15e9e6c 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,8 @@ optional arguments: The output ID --json-file JSON_FILE JSON file with parameter + --pretty + Pretty print result (if supported) else plain JSON ``` Examples: diff --git a/table_model.py b/table_model.py new file mode 100644 index 0000000..20b6bc4 --- /dev/null +++ b/table_model.py @@ -0,0 +1,70 @@ + +# The MIT License (MIT) +# +# Copyright (C) 2025 - Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""TableModel class file.""" + +from tabulate import tabulate + +import pandas as pd + + +# pylint: disable=too-few-public-methods +class TableModel: + """TreeModel class implementation.""" + + def __init__(self, model, headers=None): + self._headers = headers + self._model = model + + def print(self): + """Render this tree model.""" + frame = {} + data = [] + low_index = self._model.low_index + for line in self._model.lines: + row = [] + row.append(low_index) + low_index += 1 + for cell in line.cells: + row.append(cell.content) + data.append(row) + + headers = [] + headers.append("Index") + + if self._headers is not None: + for col_id in self._model.column_ids: + headers.append(self._headers[col_id].name) + + # for header in self._headers: + # headers.append(header.name) + if len(headers) == len(data[0]): + frame = pd.DataFrame(data, columns=headers) + else: + frame = pd.DataFrame(data) + else: + frame = pd.DataFrame(data) + # print(frame.to_string()) + #frame.to_csv('output.csv', index=False, sep='\t') + print(tabulate(frame.values, headers, tablefmt="fancy_grid")) + diff --git a/tsp/entry_model.py b/tsp/entry_model.py index a74b3ae..63ebf93 100644 --- a/tsp/entry_model.py +++ b/tsp/entry_model.py @@ -60,12 +60,15 @@ def __init__(self, params, model_type=ModelType.XY_TREE): def __repr__(self) -> str: return 'EntryModel({})'.format(', '.join(str(entry) for entry in self.entries)) + + def to_json(self): + return json.dumps(self, cls=EntryModelEncoder, indent=4) class EntryModelEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, EntryModel): return { - 'headers': [EntryHeaderEncoder().default(header) for header in obj.headers], - 'entries': [TimeGraphEntryEncoder().default(entry) if isinstance(entry, TimeGraphEntry) else EntryEncoder().default(entry) for entry in obj.entries] + HEADER_KEY: [EntryHeaderEncoder().default(header) for header in obj.headers], + ENTRIES_KEY: [TimeGraphEntryEncoder().default(entry) if isinstance(entry, TimeGraphEntry) else EntryEncoder().default(entry) for entry in obj.entries] } return super().default(obj) diff --git a/tsp/virtual_table_header_model.py b/tsp/virtual_table_header_model.py index 444e52c..7b3a50d 100644 --- a/tsp/virtual_table_header_model.py +++ b/tsp/virtual_table_header_model.py @@ -22,6 +22,8 @@ """Virtual table header model file.""" +import json + COLUMN_ID_KEY = "id" COLUMN_NAME_KEY = "name" COLUMN_DESCRIPTION_KEY = "description" @@ -49,6 +51,17 @@ def print(self): for column in self.columns: column.print() + def to_json(self): + return json.dumps(self, cls=VirtualTableHeaderModelEncoder, indent=4) + +class VirtualTableHeaderModelEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, VirtualTableHeaderModel): + # result = {} + # result['model'] = [ VirtualTableHeaderColumnModelEncoder().default(column) for column in obj.columns ] + return [ VirtualTableHeaderColumnModelEncoder().default(column) for column in obj.columns ] + return super().default(obj) + class VirtualTableHeaderColumnModel: ''' Virtual table header column model that will be returned by the server @@ -87,4 +100,18 @@ def print(self): print(" name: " + str(self.name)) print(" description: " + str(self.description)) print(" type: " + str(self.type)) - print("-" * 50) \ No newline at end of file + print("-" * 50) + + def to_json(self): + return json.dumps(self, cls=VirtualTableHeaderColumnModelEncoder, indent=4) + +class VirtualTableHeaderColumnModelEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, VirtualTableHeaderColumnModel): + result = {} + result[COLUMN_ID_KEY] = obj.id + result[COLUMN_NAME_KEY] = obj.name + result[COLUMN_DESCRIPTION_KEY] = obj.description + result[COLUMN_TYPE_KEY] = obj.type + return result + return super().default(obj) \ No newline at end of file diff --git a/tsp/virtual_table_model.py b/tsp/virtual_table_model.py index 2ccd460..2fdcc23 100644 --- a/tsp/virtual_table_model.py +++ b/tsp/virtual_table_model.py @@ -22,6 +22,7 @@ """VirtualTableModel class file.""" +import json from tsp.virtual_table_tag import VirtualTableTag SIZE_KEY = "size" @@ -71,6 +72,9 @@ def __init__(self, params): self.lines.append(VirtualTableLine(line)) del params[LINES_KEY] + def to_json(self): + return json.dumps(self, cls=VirtualTableModelEncoder, indent=4) + def print(self): print("VirtualTableModel:") print(f" size: {self.size}") @@ -81,6 +85,17 @@ def print(self): for i, line in enumerate(self.lines): line.print() +class VirtualTableModelEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, VirtualTableModel): + result = {} + result[SIZE_KEY] = obj.size + result[LOW_INDEX_KEY] = obj.low_index + result[COLUMN_IDS_KEY] = obj.column_ids + result[LINES_KEY] = [ VirtualTableLineEncoder().default(line) for line in obj.lines ] + return result + return super().default(obj) + class VirtualTableLine: ''' Virtual table line that will be returned by the server @@ -122,6 +137,9 @@ def __init__(self, params): def has_tag(self, tag): return bool(self.tags & tag) + def to_json(self): + return json.dumps(self, cls=VirtualTableLineEncoder, indent=4) + def print(self): print(f" index: {self.index}") @@ -138,6 +156,16 @@ def print(self): cell.print() print(f" {'-' * 30}") +class VirtualTableLineEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, VirtualTableLine): + result = {} + result[TABLE_LINE_INDEX_KEY] = obj.index + result[TAGS_KEY] = obj.tags.value + result[TABLE_LINE_CELLS_KEY] = [ VirtualTableLineCellEncoder().default(cell) for cell in obj.cells ] + return result + return super().default(obj) + class VirtualTableLineCell: ''' Virtual table line cell that will be returned by the server @@ -180,4 +208,16 @@ def print(self): tags_str = " | ".join(active_tags) print(f" \"tags\": \"{tags_str}\"") - print(f" {'-' * 10}") \ No newline at end of file + print(f" {'-' * 10}") + + def to_json(self): + return json.dumps(self, cls=VirtualTableLineCell, indent=4) + +class VirtualTableLineCellEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, VirtualTableLineCell): + return { + TABLE_LINE_CELL_CONTENT_KEY: obj.content, + TAGS_KEY: obj.tags.value + } + return super().default(obj) diff --git a/tsp_cli_client b/tsp_cli_client index 19b4c38..941288d 100755 --- a/tsp_cli_client +++ b/tsp_cli_client @@ -38,6 +38,7 @@ import requests from termcolor import colored from tree_model import TreeModel +from table_model import TableModel from tsp.tsp_client import TspClient TRACE_MISSING = "Trace UUID is missing" @@ -85,10 +86,18 @@ def __get_tree(uuid, outputid, treetype): if tree is None: print("Tree had no model; retry?") sys.exit(1) + + print('Successfully fetched tree') + print('-------------------------') - tree_model = TreeModel(tree.entries, tree.headers) - tree_model.print() + if (options.pretty): + tree_model = TreeModel(tree.entries, tree.headers) + tree_model.print() + sys.exit(0) + + print(tree.to_json()) sys.exit(0) + else: sys.exit(1) else: @@ -220,6 +229,8 @@ if __name__ == "__main__": parser.add_argument("--delete-output", dest="delete_output", help="Delete derived output", metavar="DERIVED_OUTPUT_ID") parser.add_argument("--output-id", dest="output_id", help="The output ID") parser.add_argument("--json-file", dest="json_file", help="JSON file with parameter") + parser.add_argument("--pretty", action='store_true', dest="pretty", help="Pretty print result (if supported) else plain JSON") + argcomplete.autocomplete(parser) options = parser.parse_args() @@ -431,8 +442,15 @@ if __name__ == "__main__": options.uuid, options.get_virtual_table_columns) if response.status_code == 200: + print('Successfully fetched virtual table columns') + print('------------------------------------------') + model = response.model.model - model.print() + if (options.pretty): + model.print() + sys.exit(0) + + print(model.to_json()) sys.exit(0) else: sys.exit(1) @@ -471,8 +489,26 @@ if __name__ == "__main__": response = tsp_client.fetch_virtual_table_lines(options.uuid, options.get_virtual_table_lines, parameters) if response.status_code == 200: - model = response.model.model - model.print() + print('Successfully fetched virtual table lines') + print('----------------------------------------') + + columns_response = tsp_client.fetch_virtual_table_columns( + options.uuid, options.get_virtual_table_lines) + + headers = {} + if response.status_code == 200: + headers = columns_response.model.model.columns + + if (options.pretty): + table_model = TableModel(response.model.model, headers) + table_model.print() + sys.exit(0) + + print(response.model.model.to_json()) + sys.exit(0) + +# python_object = json.loads(response.status_text) +# print(json.dumps(python_object, indent=4)) sys.exit(0) else: sys.exit(1)