import base64
import html
import json
import os
import tempfile
import time
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
import PIL.Image
from ..utils import html_utils
REPORT_VERSION = 1.0
[docs]
class DracReportBuilder(object):
"""Build a json and/or html file compatible with Drac dataset reports.
Example:
report = DracReportBuilder(
dataset_type="Characterisation",
dataset_title="Characterisation Results",
)
report.add_info("This is a demo info item.")
report.add_table(
title="Sample Table",
columns=["Name", "Value"],
data=[["Alpha", 1], ["Beta", 2]],
)
report.add_image("/path/to/image.png", image_title="Preview")
report.render_json("report.json")
"""
def __init__(self, dataset_type: str, dataset_title: str = "") -> None:
self.image_list: Optional[Dict[str, Any]] = None
self.dict_report: Dict[str, Any] = {}
self.dict_report["version"] = REPORT_VERSION
self.dict_report["type"] = dataset_type
self.dict_report["title"] = dataset_title
self.dict_report["items"] = []
[docs]
def set_title(self, dataset_title: str) -> None:
self.dict_report["title"] = dataset_title
[docs]
def add_info(self, info_text: str) -> None:
self.dict_report["items"].append({"type": "info", "value": info_text})
[docs]
def add_warning(self, info_text: str) -> None:
self.dict_report["items"].append({"type": "warning", "value": info_text})
[docs]
def start_image_list(self) -> None:
self.image_list = {}
self.image_list["type"] = "images"
self.image_list["items"] = []
[docs]
def end_image_list(self) -> None:
self.dict_report["items"].append(self.image_list)
self.image_list = None
def _create_image_item(
self,
path_to_image: str,
image_title: str,
path_to_thumbnail_image: Optional[str] = None,
thumbnail_height: Optional[int] = None,
thumbnail_width: Optional[int] = None,
) -> Dict[str, Any]:
item: Dict[str, Any] = {}
item["type"] = "image"
item["suffix"] = path_to_image.split(".")[-1]
item["title"] = image_title
with PIL.Image.open(path_to_image) as image:
item["xsize"] = image.size[0]
item["ysize"] = image.size[1]
with open(path_to_image, "rb") as image_file:
item["value"] = base64.b64encode(image_file.read()).decode("utf-8")
if path_to_thumbnail_image is None:
if thumbnail_height is not None and thumbnail_width is not None:
item["thumbnailSuffix"] = path_to_image.split(".")[-1]
item["thumbnailXsize"] = thumbnail_height
item["thumbnailYsize"] = thumbnail_width
with open(path_to_image, "rb") as image_file:
item["thumbnailValue"] = base64.b64encode(image_file.read())
else:
item["thumbnailSuffix"] = path_to_thumbnail_image.split(".")[-1]
with PIL.Image.open(path_to_thumbnail_image) as thumbnail_im:
item["thumbnailXsize"] = thumbnail_im.size[0]
item["thumbnailYsize"] = thumbnail_im.size[1]
with open(path_to_thumbnail_image, "rb") as thumbnail_file:
item["thumbnailValue"] = base64.b64encode(thumbnail_file.read()).decode(
"utf-8"
)
return item
[docs]
def add_image(
self,
path_to_image: str,
image_title: str = "",
path_to_thumbnail_image: Optional[str] = None,
thumbnail_height: Optional[int] = None,
thumbnail_width: Optional[int] = None,
) -> None:
if self.image_list is None:
self.dict_report["items"].append(
self._create_image_item(
path_to_image,
image_title,
path_to_thumbnail_image,
thumbnail_height,
thumbnail_width,
)
)
else:
self.image_list["items"].append(
self._create_image_item(
path_to_image,
image_title,
path_to_thumbnail_image,
thumbnail_height,
thumbnail_width,
)
)
[docs]
def add_table(
self,
title: str,
columns: List[str],
data: List[List[Any]],
orientation: str = "horizontal",
) -> None:
item: Dict[str, Any] = {}
item["type"] = "table"
item["title"] = title
item["columns"] = columns
item["data"] = data
item["orientation"] = orientation
self.dict_report["items"].append(item)
[docs]
def add_log_file(self, title: str, link_text: str, path_to_log_file: str) -> None:
item: Dict[str, Any] = {}
item["type"] = "logFile"
item["title"] = title
item["link_text"] = link_text
with open(path_to_log_file, "r", encoding="utf-8", errors="ignore") as log_file:
item["logText"] = log_file.read()
self.dict_report["items"].append(item)
[docs]
def get_dict_report(self) -> Dict[str, Any]:
return self.dict_report
[docs]
def render_json(self, path_to_json_dir: str) -> str:
path_to_json_file = os.path.join(path_to_json_dir, "report.json")
with open(path_to_json_file, "w") as json_file:
json_file.write(json.dumps(self.dict_report, indent=4))
return path_to_json_file
[docs]
def escape_characters(self, str_value: str) -> str:
str_value = html.escape(str_value)
str_value = str_value.replace(str("Å"), "Å")
str_value = str_value.replace(str("°"), "°")
str_value = str_value.replace("\n", "<br>")
return str_value
[docs]
def render_html(
self, path_to_html_dir: str, name_of_index_file: str = "index.html"
) -> str:
page = html_utils.HtmlPage(mode="loose_html")
page.init(
title=self.dict_report["title"], footer="Generated on %s" % time.asctime()
)
page.div(align_="LEFT")
page.h1()
page.strong(self.dict_report["title"])
page.h1.close()
page.div.close()
for item in self.dict_report["items"]:
if "value" in item:
item_value = item["value"]
if "title" in item:
item_title = self.escape_characters(item["title"])
if item["type"] == "info":
page.p(item_value)
if item["type"] == "warning":
page.font(_color="red", size="+1")
page.p()
page.strong(item_value)
page.p.close()
page.font.close()
elif item["type"] == "image":
self._render_image(page, item, path_to_html_dir)
page.br()
page.p(item_title)
elif item["type"] == "images":
page.table()
page.tr(align_="CENTER")
for item in item["items"]:
item_title = self.escape_characters(item["title"])
page.td()
page.table()
page.tr()
page.td()
self._render_image(page, item, path_to_html_dir)
page.td.close()
page.tr.close()
page.tr(align_="CENTER")
page.td(item_title)
page.tr.close()
page.table.close()
page.td.close()
page.tr.close()
page.table.close()
page.br()
elif item["type"] == "table":
title_bg_colour = "#99c2ff"
column_title_bg_colour = "#ffffff"
data_bg_colour = "#e6f0ff"
page.table(
border_="1",
cellpadding_="2",
width="100%",
style_=(
"border: 1px solid black; border-collapse: collapse; margin:"
" 0px; font-size: 12px"
),
)
page.tr(align_="LEFT")
# page.strong(item_title)
page.th(
item_title,
bgcolor_=title_bg_colour,
align_="LEFT",
style_=(
"border: 1px solid black; border-collapse: collapse; padding:"
" 5px;"
),
colspan_=str(len(item["columns"])),
)
page.tr.close()
if "orientation" in item and item["orientation"] == "vertical":
for index1 in range(len(item["columns"])):
item_column = self.escape_characters(item["columns"][index1])
page.tr(align_="LEFT")
page.th(
item_column,
bgcolor_=column_title_bg_colour,
align_="LEFT",
style_=(
"border: 1px solid black; border-collapse: collapse;"
" padding: 5px;"
),
)
for index2 in range(len(item["data"])):
item_data = self.escape_characters(
str(item["data"][index2][index1])
)
page.th(
item_data,
bgcolor_=data_bg_colour,
style_="padding: 5px;",
)
page.tr.close()
else:
page.tr(
align_="LEFT",
bgcolor_=column_title_bg_colour,
style_=(
"border: 1px solid black; border-collapse: collapse;"
" padding: 5px;"
),
)
for column in item["columns"]:
item_column = self.escape_characters(column)
page.th(
item_column,
style_=(
"border: 1px solid black; border-collapse: collapse;"
" padding: 5px;"
),
)
page.tr.close()
for list_row in item["data"]:
page.tr(
align_="LEFT",
bgcolor_=data_bg_colour,
style_="border-collapse: collapse; padding: 5px;",
)
for cell in list_row:
item_cell = self.escape_characters(str(cell))
page.th(
item_cell,
style_=(
"border: 1px solid black; border-collapse:"
" collapse; padding: 5px;"
),
)
page.tr.close()
page.table.close()
page.br()
elif item["type"] == "logFile":
path_to_log_html = os.path.join(path_to_html_dir, item_title + ".html")
if os.path.exists(path_to_log_html):
fd, path_to_log_html = tempfile.mkstemp(
suffix=".html",
prefix=item_title.replace(" ", "_") + "_",
dir=path_to_html_dir,
)
os.close(fd)
page_log_html = html_utils.HtmlPage()
page_log_html.h1(item_title)
page_log_html.pre(page.escape(item["logText"]))
with open(path_to_log_html, "w") as log_html_file:
log_html_file.write(str(page_log_html))
os.chmod(path_to_log_html, 0o644)
page.p()
page.a(item["link_text"], href_=os.path.basename(path_to_log_html))
page.p.close()
html = str(page)
page_path = os.path.join(path_to_html_dir, name_of_index_file)
with open(page_path, "w") as file_page:
file_page.write(html)
return page_path
def _render_image(
self, page: Any, item: Dict[str, Any], path_to_html_dir: str
) -> None:
image_name = item["title"].replace(" ", "_")
path_to_image = os.path.join(
path_to_html_dir, "{0}.{1}".format(image_name, item["suffix"])
)
if os.path.exists(path_to_image):
fd, path_to_image = tempfile.mkstemp(
suffix="." + item["suffix"],
prefix=image_name + "_",
dir=path_to_html_dir,
)
os.close(fd)
with open(path_to_image, "wb") as image_file:
image_file.write(base64.b64decode(item["value"]))
os.chmod(path_to_image, 0o644)
if "thumbnailValue" in item:
thumbnail_image_name = image_name + "_thumbnail"
path_to_thumbnail_image = os.path.join(
path_to_html_dir, "{0}.{1}".format(thumbnail_image_name, item["suffix"])
)
if os.path.exists(path_to_thumbnail_image):
fd, path_to_thumbnail_image = tempfile.mkstemp(
suffix="." + item["suffix"],
prefix=thumbnail_image_name + "_",
dir=path_to_html_dir,
)
os.close(fd)
with open(path_to_thumbnail_image, "wb") as thumbnail_file:
thumbnail_file.write(base64.b64decode(item["thumbnailValue"]))
os.chmod(path_to_thumbnail_image, 0o644)
page_reference_image = html_utils.HtmlPage(mode="loose_html")
page_reference_image.init(
title=image_name, footer="Generated on %s" % time.asctime()
)
page_reference_image.h1(image_name)
page_reference_image.br()
page_reference_image.img(
src=os.path.basename(path_to_image),
title=image_name,
width=item["xsize"],
height=item["ysize"],
)
page_reference_image.br()
path_page_reference_image = os.path.join(
path_to_html_dir, "{0}.html".format(image_name)
)
with open(path_page_reference_image, "w") as file_page:
file_page.write(str(page_reference_image))
page.a(href=os.path.basename(path_page_reference_image))
page.img(
src=os.path.basename(path_to_thumbnail_image),
title=image_name,
width=item["thumbnailXsize"],
height=item["thumbnailYsize"],
)
page.a.close()
else:
page.img(
src=os.path.basename(path_to_image),
title=item["title"],
width=item["xsize"],
height=item["ysize"],
)