fmeval.reporting.cells
1from abc import ABC 2from typing import Any, List, Optional, Union 3from io import BytesIO 4import base64 5import matplotlib.pyplot as plt 6import markdown 7from IPython import display 8 9from fmeval.reporting.constants import CENTER, ListType, MARKDOWN_EXTENSIONS, RIGHT 10from fmeval.reporting.constants import SINGLE_NEWLINE, DOUBLE_NEWLINE 11 12 13class Cell(ABC): 14 """ 15 Base class for a report cell. 16 """ 17 18 19class MarkdownCell(Cell): 20 """ 21 Base class representing a markdown cell. 22 """ 23 24 content: List[Union[str, "MarkdownCell"]] 25 26 def __init__(self, *args): 27 """ 28 Input may be strings or MarkdownCells. 29 Examples: 30 assert str(MarkdownCell("# Hello1")) == "# Hello1" 31 assert str(MarkdownCell("# Hello1", "# Hello2")) == "# Hello1 \n\n# Hello2" 32 assert str(MarkdownCell(MarkdownCell("# Hello1"), "# Hello2") == "# Hello1 \n\n# Hello2" 33 """ 34 assert all(isinstance(arg, str) or isinstance(arg, MarkdownCell) for arg in args) 35 self.content = list(args) 36 37 def __str__(self): 38 return DOUBLE_NEWLINE.join([str(s) for s in self.content]) 39 40 def show(self): # pragma: no cover 41 """ 42 Displays the cell content in an IPython notebook cell. 43 """ 44 content = markdown.markdown(self.__str__(), extensions=MARKDOWN_EXTENSIONS) 45 return display.HTML(content) 46 47 48class HeadingCell(MarkdownCell): 49 """ 50 This class represents a Markdown heading. 51 """ 52 53 def __init__(self, text: str, level: int): 54 """ 55 :param text: The text for this header 56 :param level: The heading level 57 """ 58 super().__init__(f"{'#' * level} {text}") 59 60 61class BoldCell(MarkdownCell): 62 """ 63 This class represents a bold piece of text. 64 """ 65 66 def __init__(self, text): 67 super().__init__(f"**{text}**") 68 69 70class ListCell(MarkdownCell): 71 """ 72 Creates a bulleted or numbered list. 73 """ 74 75 def __init__(self, items: List[str], list_type: ListType): 76 """ 77 :param items: A list of strings where each string represents one item in the list. 78 :param list_type: Whether the list is bulleted or numbered. 79 """ 80 if list_type == ListType.NUMBERED: 81 list_content = SINGLE_NEWLINE.join(f"{idx}. {value}" for idx, value in enumerate(items, start=1)) 82 else: 83 list_content = SINGLE_NEWLINE.join(f"* {value}" for value in items) 84 super().__init__(list_content) 85 86 87class ColumnsLayoutCell(MarkdownCell): 88 """ 89 This class creates a multi-column layout cell 90 """ 91 92 def __init__(self, columns: List[List[Any]]): 93 """ 94 :param columns: A list of Lists of strings or MarkdownCells, where each inner list is one column 95 """ 96 div_begin = '<div class="row" markdown="1">' 97 div_end = "</div>" 98 col_div = f'<div class="column" markdown="1" style="float: left;width: {str(int(100//len(columns)))}%;">\n' 99 result = "".join( 100 [col_div + SINGLE_NEWLINE.join([str(item) for item in col]) + SINGLE_NEWLINE + div_end for col in columns] 101 ) 102 super().__init__(div_begin, result, div_end, ' <br style="clear:both" />') 103 104 105class FigureCell(MarkdownCell): 106 """ 107 This class represents a MarkdownCell containing HTML for a Pyplot Figure. 108 """ 109 110 def __init__( 111 self, 112 fig: plt.Figure, 113 width: Optional[str] = None, 114 height: Optional[str] = None, 115 center: Optional[bool] = True, 116 ): 117 """ 118 Initializes a FigureCell. 119 120 :param fig: The Pyplot figure that this cell represents. 121 :param width: See _html_wrapper docstring 122 :param height: See _html_wrapper docstring 123 :param center: if the figure is center aligned 124 """ 125 encoded = FigureCell._encode(fig) 126 html = FigureCell._html_wrapper(encoded=encoded, height=height, width=width, center=center) 127 super().__init__(html) 128 129 @staticmethod 130 def _encode(fig: plt.Figure) -> bytes: 131 """ 132 Returns the base64 encoding of `fig`. 133 134 :param fig: The Pyplot Figure to be encoded. 135 :returns: The base64 encoding of `fig` 136 """ 137 buffer = BytesIO() 138 fig.tight_layout() 139 fig.savefig(buffer, format="png", bbox_inches="tight") 140 plt.close(fig) # save memory by closing the figure 141 buffer.seek(0) 142 encoded = base64.b64encode(buffer.getvalue()) 143 return encoded 144 145 @staticmethod 146 def _html_wrapper( 147 encoded: bytes, height: Optional[str], width: Optional[str], center: Optional[bool] = True 148 ) -> str: 149 """ 150 Decodes the provided base64-encoded bytes (which will generally correspond to 151 an encoded Pyplot Figure) as a string, then wraps it in HTML. 152 153 :param encoded: The base64 encoded bytes to be wrapped 154 :param height: The `height` HTML attribute 155 :param width: The `width` HTML attribute 156 :param center: Horizontal centering of the figure 157 :returns: An HTML string representing the wrapped encoded string 158 """ 159 style = 'style="display: block;' 160 if center: # pragma: no branch 161 style += "margin-left:auto; margin-right: auto;" 162 if width: 163 style += f"width:{width}; " 164 if height: 165 style += f"height:{height};" 166 encoded_str = encoded.decode("utf-8") 167 html = f"<br><img {style}\" src='data:image/png;base64,{encoded_str}'><br>" 168 return html 169 170 171class BarPlotCell(FigureCell): 172 """ 173 This class represents a Pyplot bar plot figure. 174 """ 175 176 def __init__( 177 self, 178 labels: List[str], 179 heights: List[Union[int, float]], 180 color: Optional[Union[List[str], str]] = None, 181 title: str = "Title", 182 plot_height: Optional[str] = None, 183 plot_width: Optional[str] = None, 184 center: Optional[bool] = True, 185 origin: float = 0, 186 ): 187 """ 188 Initializes a BarPlotCell. 189 190 :param labels: The labels corresponding to each of the bars in the plot 191 :param heights: The heights of the bars in the plot 192 :param title: The title of the bar plot 193 :param plot_height: Height of the plot as a string 194 :param plot_width: Width the plot as a string 195 :param center: Boolean indicating if the plot should be center aligned in the page 196 """ 197 assert len(labels) == len( 198 heights 199 ), f"Number of labels in {labels} does not match number of bar heights in {heights}" 200 fig = BarPlotCell._create_bar_plot_fig(labels, heights, color=color, title=title, origin=origin) 201 super().__init__(fig, height=plot_height, width=plot_width, center=center) 202 203 @staticmethod 204 def _create_bar_plot_fig( 205 labels: List[str], 206 heights: List[Union[int, float]], 207 color: Optional[Union[List[str], str]] = None, 208 title: str = "Title", 209 set_spines_visible: bool = False, 210 set_ticks_visible: bool = False, 211 set_horizontal_grid_lines: bool = True, 212 max_bar_width: float = 0.3, 213 origin: float = 0, 214 ) -> plt.Figure: 215 fig, ax = plt.subplots() 216 heights = [height - origin for height in heights] 217 ax.bar(labels, heights, width=max_bar_width, color=color) 218 locs = ax.get_yticks() 219 ax.set_yticks(locs, [round(loc + origin, ndigits=3) for loc in locs]) 220 if origin != 0: # pragma: no cover 221 ax.axhline(0, color="gray") 222 ax.text( 223 x=1.02, 224 y=0, 225 s="unbiased model", 226 va="center", 227 ha="left", 228 bbox=dict(facecolor="w", alpha=0.5), 229 transform=ax.get_yaxis_transform(), 230 ) 231 ax.set_title(title) 232 233 # auto-format bar labels to not overlap 234 fig.autofmt_xdate() 235 if set_horizontal_grid_lines: # pragma: no branch 236 ax.grid(axis="y") 237 ax.set_axisbelow(True) 238 if not set_spines_visible: # pragma: no branch 239 ax.spines["top"].set_visible(False) 240 ax.spines["right"].set_visible(False) 241 ax.spines["bottom"].set_visible(False) 242 ax.spines["left"].set_visible(False) 243 if not set_ticks_visible: # pragma: no branch 244 plt.tick_params(axis="both", which="both", length=0) 245 246 return fig 247 248 249class TableCell(MarkdownCell): 250 """ 251 This class represents an HTML table. 252 253 Note that despite having "Cell" in its name, this class does *not* 254 represent a single cell within an HTML table, but rather the entire table. 255 The "Cell" suffix is included to match the naming convention for subclasses 256 of MarkdownCell. 257 """ 258 259 def __init__( 260 self, 261 data: List[List[Any]], 262 headers: List[str], 263 table_align: str = CENTER, 264 cell_align: str = RIGHT, 265 style: Optional[str] = None, 266 caption: Optional[str] = None, 267 ): 268 """ 269 Initializes a TableCell. 270 271 :param data: A 2D array representing tabular data 272 :param headers: The table's headers, i.e. column names 273 :param table_align: The alignment of the table within the overarching markdown 274 :param cell_align: The alignment of text within each cell of the table 275 """ 276 assert len(headers) == len(data[0]), ( 277 f"Number of headers in {headers} does not match " f"the number of columns in the data: {len(data[0])}" 278 ) 279 html = TableCell._create_table_html(data, headers, table_align, cell_align, style, caption) 280 super().__init__(html) 281 282 @staticmethod 283 def _create_table_html( 284 data: List[List[Any]], 285 headers: List[str], 286 table_align: str, 287 cell_align: str, 288 style: Optional[str], 289 caption: Optional[str], 290 ) -> str: 291 """ 292 Creates the HTML for a table. 293 294 :param data: A 2D array representing tabular data 295 :param headers: The table's headers, i.e. column names 296 :param table_align: The alignment of the table within the overarching markdown 297 :param cell_align: The alignment of text within each cell of the table 298 :returns: A string encoding the HTML for the table 299 """ 300 table_style = f'style="{style}"' if style else "" 301 html = [ 302 f"<table align={table_align} {table_style}>", 303 f'<caption style="text-align: left; padding-bottom: 15px;">{caption}</caption>' if caption else "", 304 TableCell._create_table_row(headers, cell_align, is_header=True), 305 ] 306 for row in data: 307 html.append(TableCell._create_table_row(row, cell_align)) 308 html.append("</table>") 309 return SINGLE_NEWLINE.join(html) 310 311 @staticmethod 312 def _create_table_row(row: List[Any], cell_align: str, is_header: bool = False) -> str: 313 """ 314 Creates the HTML for a single table row. 315 316 :param row: A list representing the elements in the row 317 :param cell_align: The alignment of text within each cell of the table 318 :is_header: Whether `row` corresponds to the table header 319 :returns: A string encoding the HTML for the table row 320 """ 321 tag = "th" if is_header else "td" 322 html = ["<tr>"] 323 for i, elem in enumerate(row): 324 style = f'style="text-align: {cell_align};"' if i != 0 else "" 325 html.append(f"<{tag} {style}>{elem}</{tag}>") 326 html.append("</tr>") 327 return " ".join(html)
Base class for a report cell.
20class MarkdownCell(Cell): 21 """ 22 Base class representing a markdown cell. 23 """ 24 25 content: List[Union[str, "MarkdownCell"]] 26 27 def __init__(self, *args): 28 """ 29 Input may be strings or MarkdownCells. 30 Examples: 31 assert str(MarkdownCell("# Hello1")) == "# Hello1" 32 assert str(MarkdownCell("# Hello1", "# Hello2")) == "# Hello1 \n\n# Hello2" 33 assert str(MarkdownCell(MarkdownCell("# Hello1"), "# Hello2") == "# Hello1 \n\n# Hello2" 34 """ 35 assert all(isinstance(arg, str) or isinstance(arg, MarkdownCell) for arg in args) 36 self.content = list(args) 37 38 def __str__(self): 39 return DOUBLE_NEWLINE.join([str(s) for s in self.content]) 40 41 def show(self): # pragma: no cover 42 """ 43 Displays the cell content in an IPython notebook cell. 44 """ 45 content = markdown.markdown(self.__str__(), extensions=MARKDOWN_EXTENSIONS) 46 return display.HTML(content)
Base class representing a markdown cell.
27 def __init__(self, *args): 28 """ 29 Input may be strings or MarkdownCells. 30 Examples: 31 assert str(MarkdownCell("# Hello1")) == "# Hello1" 32 assert str(MarkdownCell("# Hello1", "# Hello2")) == "# Hello1 \n\n# Hello2" 33 assert str(MarkdownCell(MarkdownCell("# Hello1"), "# Hello2") == "# Hello1 \n\n# Hello2" 34 """ 35 assert all(isinstance(arg, str) or isinstance(arg, MarkdownCell) for arg in args) 36 self.content = list(args)
Input may be strings or MarkdownCells. Examples: assert str(MarkdownCell("# Hello1")) == "# Hello1" assert str(MarkdownCell("# Hello1", "# Hello2")) == "# Hello1
Hello2"
assert str(MarkdownCell(MarkdownCell("# Hello1"), "# Hello2") == "# Hello1
Hello2"
49class HeadingCell(MarkdownCell): 50 """ 51 This class represents a Markdown heading. 52 """ 53 54 def __init__(self, text: str, level: int): 55 """ 56 :param text: The text for this header 57 :param level: The heading level 58 """ 59 super().__init__(f"{'#' * level} {text}")
This class represents a Markdown heading.
54 def __init__(self, text: str, level: int): 55 """ 56 :param text: The text for this header 57 :param level: The heading level 58 """ 59 super().__init__(f"{'#' * level} {text}")
Parameters
- text: The text for this header
- level: The heading level
Inherited Members
62class BoldCell(MarkdownCell): 63 """ 64 This class represents a bold piece of text. 65 """ 66 67 def __init__(self, text): 68 super().__init__(f"**{text}**")
This class represents a bold piece of text.
Input may be strings or MarkdownCells. Examples: assert str(MarkdownCell("# Hello1")) == "# Hello1" assert str(MarkdownCell("# Hello1", "# Hello2")) == "# Hello1
Hello2"
assert str(MarkdownCell(MarkdownCell("# Hello1"), "# Hello2") == "# Hello1
Hello2"
Inherited Members
71class ListCell(MarkdownCell): 72 """ 73 Creates a bulleted or numbered list. 74 """ 75 76 def __init__(self, items: List[str], list_type: ListType): 77 """ 78 :param items: A list of strings where each string represents one item in the list. 79 :param list_type: Whether the list is bulleted or numbered. 80 """ 81 if list_type == ListType.NUMBERED: 82 list_content = SINGLE_NEWLINE.join(f"{idx}. {value}" for idx, value in enumerate(items, start=1)) 83 else: 84 list_content = SINGLE_NEWLINE.join(f"* {value}" for value in items) 85 super().__init__(list_content)
Creates a bulleted or numbered list.
76 def __init__(self, items: List[str], list_type: ListType): 77 """ 78 :param items: A list of strings where each string represents one item in the list. 79 :param list_type: Whether the list is bulleted or numbered. 80 """ 81 if list_type == ListType.NUMBERED: 82 list_content = SINGLE_NEWLINE.join(f"{idx}. {value}" for idx, value in enumerate(items, start=1)) 83 else: 84 list_content = SINGLE_NEWLINE.join(f"* {value}" for value in items) 85 super().__init__(list_content)
Parameters
- items: A list of strings where each string represents one item in the list.
- list_type: Whether the list is bulleted or numbered.
Inherited Members
88class ColumnsLayoutCell(MarkdownCell): 89 """ 90 This class creates a multi-column layout cell 91 """ 92 93 def __init__(self, columns: List[List[Any]]): 94 """ 95 :param columns: A list of Lists of strings or MarkdownCells, where each inner list is one column 96 """ 97 div_begin = '<div class="row" markdown="1">' 98 div_end = "</div>" 99 col_div = f'<div class="column" markdown="1" style="float: left;width: {str(int(100//len(columns)))}%;">\n' 100 result = "".join( 101 [col_div + SINGLE_NEWLINE.join([str(item) for item in col]) + SINGLE_NEWLINE + div_end for col in columns] 102 ) 103 super().__init__(div_begin, result, div_end, ' <br style="clear:both" />')
This class creates a multi-column layout cell
93 def __init__(self, columns: List[List[Any]]): 94 """ 95 :param columns: A list of Lists of strings or MarkdownCells, where each inner list is one column 96 """ 97 div_begin = '<div class="row" markdown="1">' 98 div_end = "</div>" 99 col_div = f'<div class="column" markdown="1" style="float: left;width: {str(int(100//len(columns)))}%;">\n' 100 result = "".join( 101 [col_div + SINGLE_NEWLINE.join([str(item) for item in col]) + SINGLE_NEWLINE + div_end for col in columns] 102 ) 103 super().__init__(div_begin, result, div_end, ' <br style="clear:both" />')
Parameters
- columns: A list of Lists of strings or MarkdownCells, where each inner list is one column
Inherited Members
106class FigureCell(MarkdownCell): 107 """ 108 This class represents a MarkdownCell containing HTML for a Pyplot Figure. 109 """ 110 111 def __init__( 112 self, 113 fig: plt.Figure, 114 width: Optional[str] = None, 115 height: Optional[str] = None, 116 center: Optional[bool] = True, 117 ): 118 """ 119 Initializes a FigureCell. 120 121 :param fig: The Pyplot figure that this cell represents. 122 :param width: See _html_wrapper docstring 123 :param height: See _html_wrapper docstring 124 :param center: if the figure is center aligned 125 """ 126 encoded = FigureCell._encode(fig) 127 html = FigureCell._html_wrapper(encoded=encoded, height=height, width=width, center=center) 128 super().__init__(html) 129 130 @staticmethod 131 def _encode(fig: plt.Figure) -> bytes: 132 """ 133 Returns the base64 encoding of `fig`. 134 135 :param fig: The Pyplot Figure to be encoded. 136 :returns: The base64 encoding of `fig` 137 """ 138 buffer = BytesIO() 139 fig.tight_layout() 140 fig.savefig(buffer, format="png", bbox_inches="tight") 141 plt.close(fig) # save memory by closing the figure 142 buffer.seek(0) 143 encoded = base64.b64encode(buffer.getvalue()) 144 return encoded 145 146 @staticmethod 147 def _html_wrapper( 148 encoded: bytes, height: Optional[str], width: Optional[str], center: Optional[bool] = True 149 ) -> str: 150 """ 151 Decodes the provided base64-encoded bytes (which will generally correspond to 152 an encoded Pyplot Figure) as a string, then wraps it in HTML. 153 154 :param encoded: The base64 encoded bytes to be wrapped 155 :param height: The `height` HTML attribute 156 :param width: The `width` HTML attribute 157 :param center: Horizontal centering of the figure 158 :returns: An HTML string representing the wrapped encoded string 159 """ 160 style = 'style="display: block;' 161 if center: # pragma: no branch 162 style += "margin-left:auto; margin-right: auto;" 163 if width: 164 style += f"width:{width}; " 165 if height: 166 style += f"height:{height};" 167 encoded_str = encoded.decode("utf-8") 168 html = f"<br><img {style}\" src='data:image/png;base64,{encoded_str}'><br>" 169 return html
This class represents a MarkdownCell containing HTML for a Pyplot Figure.
111 def __init__( 112 self, 113 fig: plt.Figure, 114 width: Optional[str] = None, 115 height: Optional[str] = None, 116 center: Optional[bool] = True, 117 ): 118 """ 119 Initializes a FigureCell. 120 121 :param fig: The Pyplot figure that this cell represents. 122 :param width: See _html_wrapper docstring 123 :param height: See _html_wrapper docstring 124 :param center: if the figure is center aligned 125 """ 126 encoded = FigureCell._encode(fig) 127 html = FigureCell._html_wrapper(encoded=encoded, height=height, width=width, center=center) 128 super().__init__(html)
Initializes a FigureCell.
Parameters
- fig: The Pyplot figure that this cell represents.
- width: See _html_wrapper docstring
- height: See _html_wrapper docstring
- center: if the figure is center aligned
Inherited Members
172class BarPlotCell(FigureCell): 173 """ 174 This class represents a Pyplot bar plot figure. 175 """ 176 177 def __init__( 178 self, 179 labels: List[str], 180 heights: List[Union[int, float]], 181 color: Optional[Union[List[str], str]] = None, 182 title: str = "Title", 183 plot_height: Optional[str] = None, 184 plot_width: Optional[str] = None, 185 center: Optional[bool] = True, 186 origin: float = 0, 187 ): 188 """ 189 Initializes a BarPlotCell. 190 191 :param labels: The labels corresponding to each of the bars in the plot 192 :param heights: The heights of the bars in the plot 193 :param title: The title of the bar plot 194 :param plot_height: Height of the plot as a string 195 :param plot_width: Width the plot as a string 196 :param center: Boolean indicating if the plot should be center aligned in the page 197 """ 198 assert len(labels) == len( 199 heights 200 ), f"Number of labels in {labels} does not match number of bar heights in {heights}" 201 fig = BarPlotCell._create_bar_plot_fig(labels, heights, color=color, title=title, origin=origin) 202 super().__init__(fig, height=plot_height, width=plot_width, center=center) 203 204 @staticmethod 205 def _create_bar_plot_fig( 206 labels: List[str], 207 heights: List[Union[int, float]], 208 color: Optional[Union[List[str], str]] = None, 209 title: str = "Title", 210 set_spines_visible: bool = False, 211 set_ticks_visible: bool = False, 212 set_horizontal_grid_lines: bool = True, 213 max_bar_width: float = 0.3, 214 origin: float = 0, 215 ) -> plt.Figure: 216 fig, ax = plt.subplots() 217 heights = [height - origin for height in heights] 218 ax.bar(labels, heights, width=max_bar_width, color=color) 219 locs = ax.get_yticks() 220 ax.set_yticks(locs, [round(loc + origin, ndigits=3) for loc in locs]) 221 if origin != 0: # pragma: no cover 222 ax.axhline(0, color="gray") 223 ax.text( 224 x=1.02, 225 y=0, 226 s="unbiased model", 227 va="center", 228 ha="left", 229 bbox=dict(facecolor="w", alpha=0.5), 230 transform=ax.get_yaxis_transform(), 231 ) 232 ax.set_title(title) 233 234 # auto-format bar labels to not overlap 235 fig.autofmt_xdate() 236 if set_horizontal_grid_lines: # pragma: no branch 237 ax.grid(axis="y") 238 ax.set_axisbelow(True) 239 if not set_spines_visible: # pragma: no branch 240 ax.spines["top"].set_visible(False) 241 ax.spines["right"].set_visible(False) 242 ax.spines["bottom"].set_visible(False) 243 ax.spines["left"].set_visible(False) 244 if not set_ticks_visible: # pragma: no branch 245 plt.tick_params(axis="both", which="both", length=0) 246 247 return fig
This class represents a Pyplot bar plot figure.
177 def __init__( 178 self, 179 labels: List[str], 180 heights: List[Union[int, float]], 181 color: Optional[Union[List[str], str]] = None, 182 title: str = "Title", 183 plot_height: Optional[str] = None, 184 plot_width: Optional[str] = None, 185 center: Optional[bool] = True, 186 origin: float = 0, 187 ): 188 """ 189 Initializes a BarPlotCell. 190 191 :param labels: The labels corresponding to each of the bars in the plot 192 :param heights: The heights of the bars in the plot 193 :param title: The title of the bar plot 194 :param plot_height: Height of the plot as a string 195 :param plot_width: Width the plot as a string 196 :param center: Boolean indicating if the plot should be center aligned in the page 197 """ 198 assert len(labels) == len( 199 heights 200 ), f"Number of labels in {labels} does not match number of bar heights in {heights}" 201 fig = BarPlotCell._create_bar_plot_fig(labels, heights, color=color, title=title, origin=origin) 202 super().__init__(fig, height=plot_height, width=plot_width, center=center)
Initializes a BarPlotCell.
Parameters
- labels: The labels corresponding to each of the bars in the plot
- heights: The heights of the bars in the plot
- title: The title of the bar plot
- plot_height: Height of the plot as a string
- plot_width: Width the plot as a string
- center: Boolean indicating if the plot should be center aligned in the page
Inherited Members
250class TableCell(MarkdownCell): 251 """ 252 This class represents an HTML table. 253 254 Note that despite having "Cell" in its name, this class does *not* 255 represent a single cell within an HTML table, but rather the entire table. 256 The "Cell" suffix is included to match the naming convention for subclasses 257 of MarkdownCell. 258 """ 259 260 def __init__( 261 self, 262 data: List[List[Any]], 263 headers: List[str], 264 table_align: str = CENTER, 265 cell_align: str = RIGHT, 266 style: Optional[str] = None, 267 caption: Optional[str] = None, 268 ): 269 """ 270 Initializes a TableCell. 271 272 :param data: A 2D array representing tabular data 273 :param headers: The table's headers, i.e. column names 274 :param table_align: The alignment of the table within the overarching markdown 275 :param cell_align: The alignment of text within each cell of the table 276 """ 277 assert len(headers) == len(data[0]), ( 278 f"Number of headers in {headers} does not match " f"the number of columns in the data: {len(data[0])}" 279 ) 280 html = TableCell._create_table_html(data, headers, table_align, cell_align, style, caption) 281 super().__init__(html) 282 283 @staticmethod 284 def _create_table_html( 285 data: List[List[Any]], 286 headers: List[str], 287 table_align: str, 288 cell_align: str, 289 style: Optional[str], 290 caption: Optional[str], 291 ) -> str: 292 """ 293 Creates the HTML for a table. 294 295 :param data: A 2D array representing tabular data 296 :param headers: The table's headers, i.e. column names 297 :param table_align: The alignment of the table within the overarching markdown 298 :param cell_align: The alignment of text within each cell of the table 299 :returns: A string encoding the HTML for the table 300 """ 301 table_style = f'style="{style}"' if style else "" 302 html = [ 303 f"<table align={table_align} {table_style}>", 304 f'<caption style="text-align: left; padding-bottom: 15px;">{caption}</caption>' if caption else "", 305 TableCell._create_table_row(headers, cell_align, is_header=True), 306 ] 307 for row in data: 308 html.append(TableCell._create_table_row(row, cell_align)) 309 html.append("</table>") 310 return SINGLE_NEWLINE.join(html) 311 312 @staticmethod 313 def _create_table_row(row: List[Any], cell_align: str, is_header: bool = False) -> str: 314 """ 315 Creates the HTML for a single table row. 316 317 :param row: A list representing the elements in the row 318 :param cell_align: The alignment of text within each cell of the table 319 :is_header: Whether `row` corresponds to the table header 320 :returns: A string encoding the HTML for the table row 321 """ 322 tag = "th" if is_header else "td" 323 html = ["<tr>"] 324 for i, elem in enumerate(row): 325 style = f'style="text-align: {cell_align};"' if i != 0 else "" 326 html.append(f"<{tag} {style}>{elem}</{tag}>") 327 html.append("</tr>") 328 return " ".join(html)
This class represents an HTML table.
Note that despite having "Cell" in its name, this class does not represent a single cell within an HTML table, but rather the entire table. The "Cell" suffix is included to match the naming convention for subclasses of MarkdownCell.
260 def __init__( 261 self, 262 data: List[List[Any]], 263 headers: List[str], 264 table_align: str = CENTER, 265 cell_align: str = RIGHT, 266 style: Optional[str] = None, 267 caption: Optional[str] = None, 268 ): 269 """ 270 Initializes a TableCell. 271 272 :param data: A 2D array representing tabular data 273 :param headers: The table's headers, i.e. column names 274 :param table_align: The alignment of the table within the overarching markdown 275 :param cell_align: The alignment of text within each cell of the table 276 """ 277 assert len(headers) == len(data[0]), ( 278 f"Number of headers in {headers} does not match " f"the number of columns in the data: {len(data[0])}" 279 ) 280 html = TableCell._create_table_html(data, headers, table_align, cell_align, style, caption) 281 super().__init__(html)
Initializes a TableCell.
Parameters
- data: A 2D array representing tabular data
- headers: The table's headers, i.e. column names
- table_align: The alignment of the table within the overarching markdown
- cell_align: The alignment of text within each cell of the table