dl2-reports 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,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: dl2-reports
3
+ Version: 0.1.0
4
+ Summary: A Python API for generating Datalys2 reports
5
+ Author-email: kameronbrooks <kameron@creation-wasteland.com>
6
+ Project-URL: Homepage, https://github.com/kameronbrooks/datalys2-reporting-python-api
7
+ Project-URL: Bug Tracker, https://github.com/kameronbrooks/datalys2-reporting-python-api/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: pandas>=1.0.0
14
+
15
+ # Datalys2 Reporting Python API
16
+
17
+ A Python library to build and compile interactive HTML reports using the Datalys2 Reporting framework.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pip install dl2-reports
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```python
28
+ import pandas as pd
29
+ from dl2_reports import DL2Report
30
+
31
+ # Create a report
32
+ report = DL2Report(title="My Report")
33
+
34
+ # Add data
35
+ df = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
36
+ report.add_df("my_data", df, compress=True)
37
+
38
+ # Add a page and visual
39
+ page = report.add_page("Overview")
40
+ page.add_row().add_kpi("my_data", value_column="A", title="Metric A")
41
+
42
+ # Save to HTML
43
+ report.save("report.html")
44
+ ```
45
+
46
+ ## Documentation
47
+
48
+ For detailed information on available visuals and configuration, see [DOCUMENTATION.md](DOCUMENTATION.md).
@@ -0,0 +1,34 @@
1
+ # Datalys2 Reporting Python API
2
+
3
+ A Python library to build and compile interactive HTML reports using the Datalys2 Reporting framework.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install dl2-reports
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ import pandas as pd
15
+ from dl2_reports import DL2Report
16
+
17
+ # Create a report
18
+ report = DL2Report(title="My Report")
19
+
20
+ # Add data
21
+ df = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
22
+ report.add_df("my_data", df, compress=True)
23
+
24
+ # Add a page and visual
25
+ page = report.add_page("Overview")
26
+ page.add_row().add_kpi("my_data", value_column="A", title="Metric A")
27
+
28
+ # Save to HTML
29
+ report.save("report.html")
30
+ ```
31
+
32
+ ## Documentation
33
+
34
+ For detailed information on available visuals and configuration, see [DOCUMENTATION.md](DOCUMENTATION.md).
@@ -0,0 +1,3 @@
1
+ from .dl2_reports import DL2Report
2
+
3
+ __all__ = ["DL2Report"]
@@ -0,0 +1,210 @@
1
+ from __future__ import annotations
2
+ from typing import Dict, List, Optional, Any
3
+ import pandas as pd
4
+ import json
5
+ import datetime
6
+ import gzip
7
+ import base64
8
+
9
+
10
+ class DL2Report:
11
+ class Visual:
12
+ def __init__(self, type: str, dataset_id: Optional[str] = None, **kwargs):
13
+ self.type = type
14
+ self.dataset_id = dataset_id
15
+ self.props = kwargs
16
+
17
+ def to_dict(self) -> Dict[str, Any]:
18
+ d = {
19
+ "type": self.type,
20
+ "elementType": "visual"
21
+ }
22
+ if self.dataset_id:
23
+ d["datasetId"] = self.dataset_id
24
+
25
+ # Convert snake_case keys to camelCase for the JSON
26
+ for k, v in self.props.items():
27
+ camel_k = "".join(word.capitalize() if i > 0 else word for i, word in enumerate(k.split("_")))
28
+ d[camel_k] = v
29
+ return d
30
+
31
+ class Layout:
32
+ def __init__(self, direction: str = "row", **kwargs):
33
+ self.type = "layout"
34
+ self.direction = direction
35
+ self.children: List[DL2Report.Layout | DL2Report.Visual] = []
36
+ self.props = kwargs
37
+
38
+ def add_visual(self, type: str, dataset_id: Optional[str] = None, **kwargs) -> DL2Report.Visual:
39
+ visual = DL2Report.Visual(type, dataset_id, **kwargs)
40
+ self.children.append(visual)
41
+ return visual
42
+
43
+ def add_layout(self, direction: str = "row", **kwargs) -> DL2Report.Layout:
44
+ layout = DL2Report.Layout(direction, **kwargs)
45
+ self.children.append(layout)
46
+ return layout
47
+
48
+ def add_kpi(self, dataset_id: str, value_column: str, title: str, **kwargs) -> DL2Report.Visual:
49
+ return self.add_visual("kpi", dataset_id, value_column=value_column, title=title, **kwargs)
50
+
51
+ def add_table(self, dataset_id: str, title: Optional[str] = None, **kwargs) -> DL2Report.Visual:
52
+ return self.add_visual("table", dataset_id, title=title, **kwargs)
53
+
54
+ def add_card(self, title: str, text: str, **kwargs) -> DL2Report.Visual:
55
+ return self.add_visual("card", None, title=title, text=text, **kwargs)
56
+
57
+ def add_pie(self, dataset_id: str, category_column: str, value_column: str, **kwargs) -> DL2Report.Visual:
58
+ return self.add_visual("pie", dataset_id, category_column=category_column, value_column=value_column, **kwargs)
59
+
60
+ def add_bar(self, dataset_id: str, x_column: str, y_columns: List[str], stacked: bool = False, **kwargs) -> DL2Report.Visual:
61
+ type = "stackedBar" if stacked else "clusteredBar"
62
+ return self.add_visual(type, dataset_id, x_column=x_column, y_columns=y_columns, **kwargs)
63
+
64
+ def add_scatter(self, dataset_id: str, x_column: str, y_column: str, **kwargs) -> DL2Report.Visual:
65
+ return self.add_visual("scatter", dataset_id, x_column=x_column, y_column=y_column, **kwargs)
66
+
67
+ def to_dict(self) -> Dict[str, Any]:
68
+ d = {
69
+ "type": "layout",
70
+ "direction": self.direction,
71
+ "children": [c.to_dict() for c in self.children]
72
+ }
73
+ for k, v in self.props.items():
74
+ camel_k = "".join(word.capitalize() if i > 0 else word for i, word in enumerate(k.split("_")))
75
+ d[camel_k] = v
76
+ return d
77
+
78
+ class Page:
79
+ def __init__(self, title: str, description: Optional[str] = None):
80
+ self.title = title
81
+ self.description = description
82
+ self.rows: List[DL2Report.Layout] = []
83
+
84
+ def add_row(self, direction: str = "row", **kwargs) -> DL2Report.Layout:
85
+ row = DL2Report.Layout(direction, **kwargs)
86
+ self.rows.append(row)
87
+ return row
88
+
89
+ def to_dict(self) -> Dict[str, Any]:
90
+ d = {
91
+ "title": self.title,
92
+ "rows": [r.to_dict() for r in self.rows]
93
+ }
94
+ if self.description:
95
+ d["description"] = self.description
96
+ return d
97
+
98
+ def __init__(self, title: str, description: str = "", author: str = ""):
99
+ self.title = title
100
+ self.description = description
101
+ self.author = author
102
+ self.pages: List[DL2Report.Page] = []
103
+ self.datasets: Dict[str, Dict[str, Any]] = {}
104
+ self.compressed_datasets: Dict[str, str] = {}
105
+ self.css_url = "https://cdn.jsdelivr.net/gh/kameronbrooks/datalys2-reporting@latest/dist/dl2-style.css"
106
+ self.js_url = "https://cdn.jsdelivr.net/gh/kameronbrooks/datalys2-reporting@latest/dist/datalys2-reports.min.js"
107
+ self.meta_tags: Dict[str, str] = {}
108
+
109
+ def add_df(self, name: str, df: pd.DataFrame, format: str = "records", compress: bool = False) -> DL2Report:
110
+ """
111
+ Adds a DataFrame to the report.
112
+
113
+ :param name: Name of the dataset.
114
+ :param df: The DataFrame to add.
115
+ :param format: Data format ('records' or 'table').
116
+ :param compress: Whether to compress the data using gzip.
117
+ :return: The DL2Report instance.
118
+ """
119
+ columns = df.columns.tolist()
120
+ dtypes = []
121
+ for dtype in df.dtypes:
122
+ if pd.api.types.is_numeric_dtype(dtype):
123
+ dtypes.append("number")
124
+ elif pd.api.types.is_datetime64_any_dtype(dtype):
125
+ dtypes.append("date")
126
+ else:
127
+ dtypes.append("string")
128
+
129
+ if format == "records":
130
+ data = df.to_dict(orient="records")
131
+ else:
132
+ data = df.values.tolist()
133
+
134
+ dataset_entry = {
135
+ "id": name,
136
+ "format": format,
137
+ "columns": columns,
138
+ "dtypes": dtypes,
139
+ "data": data
140
+ }
141
+
142
+ if compress:
143
+ # Convert data to JSON string, then gzip, then base64
144
+ json_data = json.dumps(data)
145
+ compressed = gzip.compress(json_data.encode("utf-8"))
146
+ b64_data = base64.b64encode(compressed).decode("utf-8")
147
+
148
+ script_id = f"compressed-data-{name}"
149
+ self.compressed_datasets[script_id] = b64_data
150
+
151
+ dataset_entry["compression"] = "gzip"
152
+ dataset_entry["compressedData"] = script_id
153
+ dataset_entry["data"] = []
154
+
155
+ # Enable GC for compressed data
156
+ self.set_meta("gc-compressed-data", "true")
157
+
158
+ self.datasets[name] = dataset_entry
159
+ return self
160
+
161
+ def add_page(self, title: str, description: Optional[str] = None) -> DL2Report.Page:
162
+ page = DL2Report.Page(title, description)
163
+ self.pages.append(page)
164
+ return page
165
+
166
+ def set_meta(self, name: str, content: str) -> DL2Report:
167
+ self.meta_tags[name] = content
168
+ return self
169
+
170
+ def compile(self) -> str:
171
+ report_data = {
172
+ "pages": [p.to_dict() for p in self.pages],
173
+ "datasets": self.datasets
174
+ }
175
+ report_data_json = json.dumps(report_data, indent=4)
176
+
177
+ meta_html = ""
178
+ for name, content in self.meta_tags.items():
179
+ meta_html += f' <meta name="{name}" content="{content}">\n'
180
+
181
+ compressed_scripts = ""
182
+ for script_id, b64_data in self.compressed_datasets.items():
183
+ compressed_scripts += f' <script id="{script_id}" type="text/b64-gzip">{b64_data}</script>\n'
184
+
185
+ html = f"""<!DOCTYPE html>
186
+ <html lang="en">
187
+ <head>
188
+ <meta charset="UTF-8">
189
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
190
+ <title>{self.title}</title>
191
+ <meta name="description" content="{self.description}">
192
+ <meta name="author" content="{self.author}">
193
+ <meta name="last-updated" content="{datetime.datetime.now().isoformat()}">
194
+ {meta_html}
195
+ <link rel="stylesheet" href="{self.css_url}">
196
+ </head>
197
+ <body>
198
+ {compressed_scripts}
199
+ <div id="root"></div>
200
+ <script id="report-data" type="application/json">
201
+ {report_data_json}
202
+ </script>
203
+ <script src="{self.js_url}"></script>
204
+ </body>
205
+ </html>"""
206
+ return html
207
+
208
+ def save(self, filename: str):
209
+ with open(filename, "w", encoding="utf-8") as f:
210
+ f.write(self.compile())
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: dl2-reports
3
+ Version: 0.1.0
4
+ Summary: A Python API for generating Datalys2 reports
5
+ Author-email: kameronbrooks <kameron@creation-wasteland.com>
6
+ Project-URL: Homepage, https://github.com/kameronbrooks/datalys2-reporting-python-api
7
+ Project-URL: Bug Tracker, https://github.com/kameronbrooks/datalys2-reporting-python-api/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: pandas>=1.0.0
14
+
15
+ # Datalys2 Reporting Python API
16
+
17
+ A Python library to build and compile interactive HTML reports using the Datalys2 Reporting framework.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pip install dl2-reports
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```python
28
+ import pandas as pd
29
+ from dl2_reports import DL2Report
30
+
31
+ # Create a report
32
+ report = DL2Report(title="My Report")
33
+
34
+ # Add data
35
+ df = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
36
+ report.add_df("my_data", df, compress=True)
37
+
38
+ # Add a page and visual
39
+ page = report.add_page("Overview")
40
+ page.add_row().add_kpi("my_data", value_column="A", title="Metric A")
41
+
42
+ # Save to HTML
43
+ report.save("report.html")
44
+ ```
45
+
46
+ ## Documentation
47
+
48
+ For detailed information on available visuals and configuration, see [DOCUMENTATION.md](DOCUMENTATION.md).
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ dl2_reports/__init__.py
4
+ dl2_reports/dl2_reports.py
5
+ dl2_reports.egg-info/PKG-INFO
6
+ dl2_reports.egg-info/SOURCES.txt
7
+ dl2_reports.egg-info/dependency_links.txt
8
+ dl2_reports.egg-info/requires.txt
9
+ dl2_reports.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ pandas>=1.0.0
@@ -0,0 +1 @@
1
+ dl2_reports
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "dl2-reports"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name="kameronbrooks", email="kameron@creation-wasteland.com" },
10
+ ]
11
+ description = "A Python API for generating Datalys2 reports"
12
+ readme = "README.md"
13
+ requires-python = ">=3.8"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+ dependencies = [
20
+ "pandas>=1.0.0",
21
+ ]
22
+
23
+ [project.urls]
24
+ "Homepage" = "https://github.com/kameronbrooks/datalys2-reporting-python-api"
25
+ "Bug Tracker" = "https://github.com/kameronbrooks/datalys2-reporting-python-api/issues"
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["."]
29
+ include = ["dl2_reports*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+