from typing import Optional, Dict, List, Callable from django.utils.html import escape class Cell: def __init__( self, value: str, is_header: bool = False, title: Optional[str] = None, classes: Optional[str] = None, extra_data: Optional[Dict] = None, ): self.value = value self.is_header = is_header self.title = title self.classes = classes self.extra_data = extra_data def render(self) -> str: result = "" if self.is_header: result += " str: if ( self.extra_data is not None and self.extra_data.get("Cell_render_inner") is not None ): return self.extra_data.get("Cell_render_inner")( self.value, self.extra_data, ) return escape(str(self.value)) def render_title(self) -> str: if ( self.extra_data is not None and self.extra_data.get("Cell_render_title") is not None ): return self.extra_data.get("Cell_render_title")( self.value, self.title, self.extra_data, ) if self.title is None: return None return escape(str(self.title)) def render_classes(self) -> str: if ( self.extra_data is not None and self.extra_data.get("Cell_render_classes") is not None ): return self.extra_data.get("Cell_render_classes")( self.value, self.classes, self.extra_data, ) if self.classes is None: return None return escape(str(self.classes)) class Row: def __init__( self, cells: List[Cell], title: Optional[str] = None, classes: Optional[str] = None, extra_data: Optional[Dict] = None, ): self.cells = cells self.title = title self.classes = classes self.extra_data = extra_data def render(self) -> str: result = " str: result = "" for cell in self.cells: result += cell.render() return result def render_title(self) -> str: if ( self.extra_data is not None and self.extra_data.get("Row_render_title") is not None ): return self.extra_data.get("Row_render_title")( self.title, self.extra_data, ) if self.title is None: return None return escape(str(self.title)) def render_classes(self) -> str: if ( self.extra_data is not None and self.extra_data.get("Row_render_classes") is not None ): return self.extra_data.get("Row_render_classes")( self.classes, self.extra_data, ) if self.classes is None: return None return escape(str(self.classes)) class TableHeadOrBody: def __init__( self, rows: List[Row], is_head: bool = False, classes: Optional[str] = None, extra_data: Optional[Dict] = None, ): self.rows = rows self.is_head = is_head self.classes = classes self.extra_data = extra_data def render(self) -> str: result = "" if self.is_head: result += " str: result = "" for row in self.rows: result += row.render() return result def render_classes(self) -> str: if ( self.extra_data is not None and self.extra_data.get("TableHeadOrBody_render_classes") is not None ): return self.extra_data.get("TableHeadOrBody_render_classes")( self.classes, self.extra_data, ) if self.classes is None: return None return escape(str(self.classes)) class Table: def __init__( self, table_head: Optional[TableHeadOrBody], table_body: Optional[TableHeadOrBody], classes: Optional[str] = None, extra_data: Optional[Dict] = None, ): self.table_head = table_head self.table_body = table_body self.classes = classes self.extra_data = extra_data def render(self) -> str: result = " str: result = "" if self.table_head is not None: result += self.table_head.render() if self.table_body is not None: result += self.table_body.render() return result def render_classes(self) -> str: if ( self.extra_data is not None and self.extra_data.get("Table_render_classes") is not None ): return self.extra_data.get("Table_render_classes")( self.classes, self.extra_data, ) if self.classes is None: return None return escape(str(self.classes)) class Column: def __init__( self, header_name: str, attribute_name: str, header_Cell_render_inner: Optional[Callable] = None, header_Cell_render_title: Optional[Callable] = None, header_Cell_render_classes: Optional[Callable] = None, body_Cell_render_inner: Optional[Callable] = None, body_Cell_render_title: Optional[Callable] = None, body_Cell_render_classes: Optional[Callable] = None, header_title: Optional[str] = None, header_classes: Optional[str] = None, body_title: Optional[str] = None, body_classes: Optional[str] = None, ): self.header_name = header_name self.attribute_name = attribute_name self.header_Cell_render_inner = header_Cell_render_inner self.header_Cell_render_title = header_Cell_render_title self.header_Cell_render_classes = header_Cell_render_classes self.body_Cell_render_inner = body_Cell_render_inner self.body_Cell_render_title = body_Cell_render_title self.body_Cell_render_classes = body_Cell_render_classes self.header_title = header_title self.header_classes = header_classes self.body_title = body_title self.body_classes = body_classes def _get_header_extra_data(self, extra_data: Optional[Dict]) -> Optional[Dict]: if self.header_Cell_render_inner is not None: if extra_data is None: extra_data = {} extra_data["Cell_render_inner"] = self.header_Cell_render_inner if self.header_Cell_render_title is not None: if extra_data is None: extra_data = {} extra_data["Cell_render_title"] = self.header_Cell_render_title if self.header_Cell_render_classes is not None: if extra_data is None: extra_data = {} extra_data["Cell_render_classes"] = self.header_Cell_render_classes return extra_data def get_header_cell(self, extra_data: Optional[Dict] = None) -> Cell: return Cell( value=self.header_name, is_header=True, title=self.header_title, classes=self.header_classes, extra_data=self._get_header_extra_data(extra_data), ) def _get_body_extra_data( self, extra_data: Optional[Dict], data_or_object, ) -> Optional[Dict]: if extra_data is None: extra_data = {} extra_data["row"] = data_or_object if self.body_Cell_render_inner is not None: extra_data["Cell_render_inner"] = self.body_Cell_render_inner if self.body_Cell_render_title is not None: extra_data["Cell_render_title"] = self.body_Cell_render_title if self.body_Cell_render_classes is not None: extra_data["Cell_render_classes"] = self.body_Cell_render_classes return extra_data def get_body_cell(self, data_or_object, extra_data: Optional[Dict] = None) -> Cell: if hasattr(data_or_object, self.attribute_name): value = getattr(data_or_object, self.attribute_name) else: value = data_or_object.get(self.attribute_name) return Cell( value=value, is_header=False, title=self.body_title, classes=self.body_classes, extra_data=self._get_body_extra_data(extra_data, data_or_object), ) class TableDefinition: def __init__( self, columns: List[Column], data_list: List, header_row_title: Optional[str] = None, header_row_classes: Optional[str] = None, header_Row_render_title: Optional[Callable] = None, header_Row_render_classes: Optional[Callable] = None, body_row_title: Optional[str] = None, body_row_classes: Optional[str] = None, body_Row_render_title: Optional[Callable] = None, body_Row_render_classes: Optional[Callable] = None, header_classes: Optional[str] = None, header_TableHeadOrBody_render_classes: Optional[Callable] = None, body_classes: Optional[str] = None, body_TableHeadOrBody_render_classes: Optional[Callable] = None, table_classes: Optional[str] = None, Table_render_classes: Optional[Callable] = None, ): self.columns = columns self.data_list = data_list self.header_row_title = header_row_title self.header_row_classes = header_row_classes self.header_Row_render_title = header_Row_render_title self.header_Row_render_classes = header_Row_render_classes self.body_row_title = body_row_title self.body_row_classes = body_row_classes self.body_Row_render_title = body_Row_render_title self.body_Row_render_classes = body_Row_render_classes self.header_classes = header_classes self.header_TableHeadOrBody_render_classes = ( header_TableHeadOrBody_render_classes ) self.body_classes = body_classes self.body_TableHeadOrBody_render_classes = body_TableHeadOrBody_render_classes self.table_classes = table_classes self.Table_render_classes = Table_render_classes def _get_header_row_extra_data(self, extra_data: Optional[Dict]) -> Optional[Dict]: if self.header_Row_render_title is not None: if extra_data is None: extra_data = {} extra_data["Row_render_title"] = self.header_Row_render_title if self.header_Row_render_classes is not None: if extra_data is None: extra_data = {} extra_data["Row_render_classes"] = self.header_Row_render_classes return extra_data def _get_table_header_extra_data( self, extra_data: Optional[Dict], ) -> Optional[Dict]: if self.header_TableHeadOrBody_render_classes is not None: if extra_data is None: extra_data = {} extra_data["TableHeadOrBody_render_classes"] = ( self.header_TableHeadOrBody_render_classes ) return extra_data def _get_table_head(self) -> TableHeadOrBody: header_cells = [] for column in self.columns: header_cells.append(column.get_header_cell()) header_row = Row( header_cells, title=self.header_row_title, classes=self.header_row_classes, extra_data=self._get_header_row_extra_data(None), ) table_head = TableHeadOrBody( [header_row], is_head=True, classes=self.header_classes, extra_data=self._get_table_header_extra_data(None), ) return table_head def _get_body_row_extra_data(self, extra_data: Optional[Dict]) -> Optional[Dict]: if self.body_Row_render_title is not None: if extra_data is None: extra_data = {} extra_data["Row_render_title"] = self.body_Row_render_title if self.body_Row_render_classes is not None: if extra_data is None: extra_data = {} extra_data["Row_render_classes"] = self.body_Row_render_classes return extra_data def _get_table_body_extra_data(self, extra_data: Optional[Dict]) -> Optional[Dict]: if self.body_TableHeadOrBody_render_classes is not None: if extra_data is None: extra_data = {} extra_data["TableHeadOrBody_render_classes"] = ( self.body_TableHeadOrBody_render_classes ) return extra_data def _get_table_body(self) -> TableHeadOrBody: body_rows = [] for data in self.data_list: body_cells = [] for column in self.columns: body_cells.append(column.get_body_cell(data)) body_row = Row( body_cells, title=self.body_row_title, classes=self.body_row_classes, extra_data=self._get_body_row_extra_data(None), ) body_rows.append(body_row) table_body = TableHeadOrBody( body_rows, is_head=False, classes=self.body_classes, extra_data=self._get_table_body_extra_data(None), ) return table_body def _get_table_extra_data(self, extra_data: Optional[Dict]) -> Optional[Dict]: if self.Table_render_classes is not None: extra_data["Table_render_classes"] = ( self.Table_render_classes ) return extra_data def get_table(self) -> Table: table_head = self._get_table_head() table_body = self._get_table_body() return Table( table_head=table_head, table_body=table_body, classes=self.table_classes, extra_data=self._get_table_extra_data({}), )