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 += "
"
result += self.render_inner()
if self.is_header:
result += " | "
else:
result += ""
return result
def render_inner(self) -> 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 = ""
result += self.render_inner()
result += "
"
return result
def render_inner(self) -> 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 += ""
result += self.render_inner()
if self.is_head:
result += ""
else:
result += ""
return result
def render_inner(self) -> 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 = ""
result += self.render_inner()
result += "
"
return result
def render_inner(self) -> 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({}),
)