Source code for idmtools.utils.display.displays
"""
Tools around displays and formatting.
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
"""
from abc import ABCMeta, abstractmethod
from typing import Any
from tabulate import tabulate
from idmtools.utils.collections import cut_iterable_to
[docs]class IDisplaySetting(metaclass=ABCMeta):
"""
Base class for a display setting.
The child class needs to implement the :meth:`display` method.
Includes:
- header: Optional header for the display.
- field: If specified, the :meth:`get_object` will call :attr:`getattr` for this field on the object.
"""
[docs] def __init__(self, header: str = None, field: str = None):
"""
Initialize our IDisplaySetting.
Args:
header: Header for display
field: Optional field to display instead of object
"""
self.header = header
self.field = field
[docs] def get_object(self, obj: Any) -> Any:
"""
Get object or field depending if field is set.
Args:
obj: Object to get
Returns:
Either obj.field or obj depending if self.field is set
"""
return getattr(obj, self.field) if self.field else obj
[docs] @abstractmethod
def display(self, obj: Any) -> str:
"""
Display the object.
Note that the attribute (identified by self.field) should be handled with :meth:`get_object`.
Args:
obj: The object to consider for display.
Returns:
A string representing what to show.
"""
pass
[docs]class StringDisplaySetting(IDisplaySetting):
"""
Class that displays the object as string.
"""
[docs] def display(self, obj):
"""
Display object.
Args:
obj: Object to display
Returns:
String of object
"""
obj = self.get_object(obj)
return str(obj)
[docs]class DictDisplaySetting(IDisplaySetting):
"""
Class that displays a dictionary.
"""
[docs] def __init__(self, header: str = None, field: str = None, max_items: int = 10, flat: bool = False):
"""
DictDisplay.
Args:
header: Optional field header.
field: The field in the object to consider.
max_items: The maximum number of items to display.
flat: If False, display as a list; if True, display as a comma-separated list.
"""
super().__init__(header=header, field=field)
self.max_items = max_items
self.flat = flat
[docs] def display(self, obj: Any) -> str:
"""
Display a dictionary.
Args:
obj: Object to display
Returns:
String display of object
"""
# Retrieve our object
obj = self.get_object(obj)
# Slice the dictionary depending on the `max_items`
slice, remaining = cut_iterable_to(obj, self.max_items)
printout = ""
# Different display depending on `flat`
if self.flat:
printout = ", ".join(f"{k}:{v}" for k, v in slice.items())
else:
for k, v in slice.items():
printout += f"- {k}:{v}\n"
printout = printout.strip()
# If there are items remaining, display
if remaining > 0:
printout = str(printout)
printout += f"\n... and {remaining} more"
return printout
[docs]class TableDisplay(IDisplaySetting):
"""
Class that displays the object as a table.
"""
[docs] def __init__(self, columns, max_rows=5, field=None):
"""
Initialize our TableDisplay.
Args:
columns: A list of display settings.
max_rows: The maximum number of rows to display.
field: The field of the object to consider.
"""
super().__init__(field=field)
self.columns = columns
self.max_rows = max_rows
[docs] def display(self, obj) -> str:
"""
Display our object as a table.
Args:
obj: Object to display
Returns:
Table represented as a string of the object
"""
# Retrieve our object
obj = super().get_object(obj)
# Cut the rows
slice, remaining = cut_iterable_to(obj, self.max_rows)
# Create the table
rows = []
for child in slice:
rows.append([s.display(child) for s in self.columns])
printout = tabulate(rows, headers=[s.header for s in self.columns], tablefmt='psql', showindex=False)
# If there are items remaining, display
if remaining > 0:
printout = str(printout)
printout += f"\n... and {remaining} more"
return printout