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.
- dl2_reports-0.1.0/PKG-INFO +48 -0
- dl2_reports-0.1.0/README.md +34 -0
- dl2_reports-0.1.0/dl2_reports/__init__.py +3 -0
- dl2_reports-0.1.0/dl2_reports/dl2_reports.py +210 -0
- dl2_reports-0.1.0/dl2_reports.egg-info/PKG-INFO +48 -0
- dl2_reports-0.1.0/dl2_reports.egg-info/SOURCES.txt +9 -0
- dl2_reports-0.1.0/dl2_reports.egg-info/dependency_links.txt +1 -0
- dl2_reports-0.1.0/dl2_reports.egg-info/requires.txt +1 -0
- dl2_reports-0.1.0/dl2_reports.egg-info/top_level.txt +1 -0
- dl2_reports-0.1.0/pyproject.toml +29 -0
- dl2_reports-0.1.0/setup.cfg +4 -0
|
@@ -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,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
|
+
|
|
@@ -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*"]
|