enhacements to FormattedOutput (#1376)

* enhacements to FormattedOutput

* flake8 complain
This commit is contained in:
Werner Llácer 2022-08-01 10:41:44 +02:00 committed by GitHub
parent 7bf0fe3c56
commit 68d89a07df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 57 additions and 19 deletions

View File

@ -2,45 +2,83 @@ import logging
import os
import sys
from pathlib import Path
from typing import Dict, Union, List, Any
from typing import Dict, Union, List, Any, Callable
from .storage import storage
from dataclasses import asdict, is_dataclass
class FormattedOutput:
@classmethod
def values(cls, o: Any) -> Dict[str, Any]:
if hasattr(o, 'as_json'):
def values(cls, o: Any, class_formatter: str = None, filter_list: List[str] = None) -> Dict[str, Any]:
""" the original values returned a dataclass as dict thru the call to some specific methods
this version allows thru the parameter class_formatter to call a dynamicly selected formatting method.
Can transmit a filter list to the class_formatter,
"""
if class_formatter:
# if invoked per reference it has to be a standard function or a classmethod.
# A method of an instance does not make sense
if callable(class_formatter):
return class_formatter(o, filter_list)
# if is invoked by name we restrict it to a method of the class. No need to mess more
elif hasattr(o, class_formatter) and callable(getattr(o, class_formatter)):
func = getattr(o, class_formatter)
return func(filter_list)
# kept as to make it backward compatible
elif hasattr(o, 'as_json'):
return o.as_json()
elif hasattr(o, 'json'):
return o.json()
elif is_dataclass(o):
return asdict(o)
else:
return o.__dict__
@classmethod
def as_table(cls, obj: List[Any]) -> str:
def as_table(cls, obj: List[Any], class_formatter: Union[str, Callable] = None, filter_list: List[str] = None) -> str:
""" variant of as_table (subtly different code) which has two additional parameters
filter which is a list of fields which will be shon
class_formatter a special method to format the outgoing data
A general comment, the format selected for the output (a string where every data record is separated by newline)
is for compatibility with a print statement
As_table_filter can be a drop in replacement for as_table
"""
raw_data = [cls.values(o, class_formatter, filter_list) for o in obj]
# determine the maximum column size
column_width: Dict[str, int] = {}
for o in obj:
for k, v in cls.values(o).items():
column_width.setdefault(k, 0)
column_width[k] = max([column_width[k], len(str(v)), len(k)])
for o in raw_data:
for k, v in o.items():
if not filter_list or k in filter_list:
column_width.setdefault(k, 0)
column_width[k] = max([column_width[k], len(str(v)), len(k)])
if not filter_list:
filter_list = (column_width.keys())
# create the header lines
output = ''
for key, width in column_width.items():
key_list = []
for key in filter_list:
width = column_width[key]
key = key.replace('!', '')
output += key.ljust(width) + ' | '
output = output[:-3] + '\n'
key_list.append(key.ljust(width))
output += ' | '.join(key_list) + '\n'
output += '-' * len(output) + '\n'
for o in obj:
for k, v in cls.values(o).items():
if '!' in k:
v = '*' * len(str(v))
output += str(v).ljust(column_width[k]) + ' | '
output = output[:-3]
output += '\n'
# create the data lines
for record in raw_data:
obj_data = []
for key in filter_list:
width = column_width.get(key, len(key))
value = record.get(key, '')
if '!' in key:
value = '*' * width
if isinstance(value,(int, float)) or (isinstance(value, str) and value.isnumeric()):
obj_data.append(str(value).rjust(width))
else:
obj_data.append(str(value).ljust(width))
output += ' | '.join(obj_data) + '\n'
return output