Implement HTML renderer with Jinja2
This commit is contained in:
5
src/py_dvt_ate/reporting/renderers/__init__.py
Normal file
5
src/py_dvt_ate/reporting/renderers/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Report renderers for HTML and PDF output."""
|
||||
|
||||
from py_dvt_ate.reporting.renderers.html import HTMLRenderer
|
||||
|
||||
__all__ = ["HTMLRenderer"]
|
||||
100
src/py_dvt_ate/reporting/renderers/html.py
Normal file
100
src/py_dvt_ate/reporting/renderers/html.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""HTML renderer using Jinja2 templates.
|
||||
|
||||
This module provides HTML rendering for test reports using Jinja2 templating.
|
||||
Templates are loaded from the package's templates directory.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
from datetime import datetime
|
||||
from importlib import resources
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Environment, PackageLoader, select_autoescape
|
||||
|
||||
from py_dvt_ate import __version__
|
||||
from py_dvt_ate.reporting.exceptions import TemplateRenderError
|
||||
from py_dvt_ate.reporting.models import ReportData
|
||||
|
||||
|
||||
class HTMLRenderer:
|
||||
"""Renders HTML reports from ReportData using Jinja2 templates.
|
||||
|
||||
The renderer loads templates from the py_dvt_ate.reporting.templates package
|
||||
and provides methods for rendering complete HTML reports.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialise the HTML renderer with Jinja2 environment."""
|
||||
self._env = Environment(
|
||||
loader=PackageLoader("py_dvt_ate.reporting", "templates"),
|
||||
autoescape=select_autoescape(["html", "xml"]),
|
||||
)
|
||||
self._css_content: str | None = None
|
||||
|
||||
def _load_css(self) -> str:
|
||||
"""Load CSS content from the templates directory."""
|
||||
if self._css_content is None:
|
||||
templates_pkg = resources.files("py_dvt_ate.reporting.templates")
|
||||
css_file = templates_pkg.joinpath("styles.css")
|
||||
self._css_content = css_file.read_text()
|
||||
return self._css_content
|
||||
|
||||
def _load_logo(self, logo_path: Path | None) -> str | None:
|
||||
"""Load and encode logo image as base64.
|
||||
|
||||
Args:
|
||||
logo_path: Path to logo image file.
|
||||
|
||||
Returns:
|
||||
Base64-encoded image data, or None if no logo or file not found.
|
||||
"""
|
||||
if logo_path is None or not logo_path.exists():
|
||||
return None
|
||||
|
||||
try:
|
||||
with logo_path.open("rb") as f:
|
||||
return base64.b64encode(f.read()).decode("utf-8")
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
def render(self, data: ReportData) -> str:
|
||||
"""Render a test report to HTML.
|
||||
|
||||
Args:
|
||||
data: Report data containing test run, results, and charts.
|
||||
|
||||
Returns:
|
||||
Complete HTML document as a string.
|
||||
|
||||
Raises:
|
||||
TemplateRenderError: If template rendering fails.
|
||||
"""
|
||||
try:
|
||||
template = self._env.get_template("test_report.html")
|
||||
|
||||
# Format config JSON for display
|
||||
config_formatted = ""
|
||||
if data.run.config_json:
|
||||
try:
|
||||
config_dict = json.loads(data.run.config_json)
|
||||
config_formatted = json.dumps(config_dict, indent=2)
|
||||
except json.JSONDecodeError:
|
||||
config_formatted = data.run.config_json
|
||||
|
||||
# Prepare template context
|
||||
context = {
|
||||
"data": data,
|
||||
"css_content": self._load_css(),
|
||||
"logo_base64": self._load_logo(data.config.logo_path),
|
||||
"company_name": data.config.company_name,
|
||||
"version": __version__,
|
||||
"generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"config_formatted": config_formatted,
|
||||
}
|
||||
|
||||
return template.render(**context)
|
||||
|
||||
except Exception as e:
|
||||
msg = f"Failed to render HTML template: {e}"
|
||||
raise TemplateRenderError(msg) from e
|
||||
Reference in New Issue
Block a user