robo-html 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: robo-html
3
+ Version: 0.1.0
4
+ Summary: Reusable HTML report rendering utilities for automation projects
5
+ Requires-Python: >=3.12,<3.13
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.12
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "robo-html"
3
+ version = "0.1.0"
4
+ description = "Reusable HTML report rendering utilities for automation projects"
5
+ requires-python = ">=3.12,<3.13"
6
+ dependencies = []
7
+
8
+ [tool.poetry]
9
+ packages = [{ include = "robo_html" }]
10
+ exclude = ["tools/**"]
11
+
12
+ [build-system]
13
+ requires = ["poetry-core==2.5.0"]
14
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,5 @@
1
+ """Reusable HTML report rendering utilities."""
2
+
3
+ from .renderer import HtmlReportGenerator
4
+
5
+ __all__ = ["HtmlReportGenerator"]
@@ -0,0 +1,83 @@
1
+ """Render a self-contained HTML report from a template and canonical JSON data."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import copy
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any, Mapping
9
+
10
+
11
+ class HtmlReportGenerator:
12
+ """Render report data into a caller-supplied standalone HTML template.
13
+
14
+ The application owns its branding/template and canonical report data model.
15
+ ``robo-html`` owns only the generic rendering and safe JSON embedding.
16
+ """
17
+
18
+ DEFAULT_DATA_PLACEHOLDER = "__ROBO_REPORT_DATA_JSON__"
19
+
20
+ @classmethod
21
+ def _load_template(cls, template_path: Path, data_placeholder: str) -> str:
22
+ template = Path(template_path).read_text(encoding="utf-8")
23
+ if data_placeholder not in template:
24
+ raise ValueError(
25
+ f"HTML report template {template_path} does not contain "
26
+ f"the required placeholder {data_placeholder!r}."
27
+ )
28
+ return template
29
+
30
+ @staticmethod
31
+ def _prepare_data(data: Mapping[str, Any]) -> dict[str, Any]:
32
+ """Return an isolated JSON-ready copy without application mutation."""
33
+ prepared = copy.deepcopy(dict(data))
34
+ if not isinstance(prepared.get("results", []), list):
35
+ prepared["results"] = []
36
+ return prepared
37
+
38
+ @staticmethod
39
+ def _serialize_for_html(data: Mapping[str, Any]) -> str:
40
+ """Serialize JSON safely for an ``application/json`` script element."""
41
+ payload = json.dumps(data, ensure_ascii=False, default=str)
42
+ return (
43
+ payload.replace("&", "\\u0026")
44
+ .replace("<", "\\u003c")
45
+ .replace(">", "\\u003e")
46
+ .replace("\u2028", "\\u2028")
47
+ .replace("\u2029", "\\u2029")
48
+ )
49
+
50
+ @classmethod
51
+ def generate(
52
+ cls,
53
+ data: Mapping[str, Any],
54
+ *,
55
+ template_path: Path,
56
+ data_placeholder: str = DEFAULT_DATA_PLACEHOLDER,
57
+ ) -> str:
58
+ """Render one self-contained HTML document from canonical report data."""
59
+ prepared = cls._prepare_data(data)
60
+ template = cls._load_template(Path(template_path), data_placeholder)
61
+ return template.replace(
62
+ data_placeholder,
63
+ cls._serialize_for_html(prepared),
64
+ 1,
65
+ )
66
+
67
+ @classmethod
68
+ def generate_from_json(
69
+ cls,
70
+ json_path: Path,
71
+ *,
72
+ template_path: Path,
73
+ data_placeholder: str = DEFAULT_DATA_PLACEHOLDER,
74
+ ) -> str:
75
+ """Render HTML from a persisted canonical JSON report."""
76
+ data = json.loads(Path(json_path).read_text(encoding="utf-8"))
77
+ if not isinstance(data, dict):
78
+ raise ValueError(f"Report JSON must contain an object: {json_path}")
79
+ return cls.generate(
80
+ data,
81
+ template_path=template_path,
82
+ data_placeholder=data_placeholder,
83
+ )