staticdash 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
- staticdash/assets/css/style.css +11 -0
- staticdash/dashboard.py +80 -51
- staticdash-0.3.0.dist-info/METADATA +57 -0
- staticdash-0.3.0.dist-info/RECORD +8 -0
- staticdash-0.2.1.dist-info/METADATA +0 -89
- staticdash-0.2.1.dist-info/RECORD +0 -8
- {staticdash-0.2.1.dist-info → staticdash-0.3.0.dist-info}/WHEEL +0 -0
- {staticdash-0.2.1.dist-info → staticdash-0.3.0.dist-info}/top_level.txt +0 -0
staticdash/assets/css/style.css
CHANGED
@@ -174,4 +174,15 @@ table.sortable th.sorted-asc::after {
|
|
174
174
|
|
175
175
|
table.sortable th.sorted-desc::after {
|
176
176
|
content: "\25BC";
|
177
|
+
}
|
178
|
+
|
179
|
+
.minipage-row {
|
180
|
+
display: flex;
|
181
|
+
gap: 20px;
|
182
|
+
margin-bottom: 20px;
|
183
|
+
}
|
184
|
+
|
185
|
+
.minipage {
|
186
|
+
box-sizing: border-box;
|
187
|
+
padding: 10px;
|
177
188
|
}
|
staticdash/dashboard.py
CHANGED
@@ -4,15 +4,12 @@ import uuid
|
|
4
4
|
import pandas as pd
|
5
5
|
import plotly.graph_objects as go
|
6
6
|
from dominate import document
|
7
|
-
from dominate.tags import div, h1, h2, h3, h4, p, a, script, link
|
7
|
+
from dominate.tags import div, h1, h2, h3, h4, p, a, script, link
|
8
8
|
from dominate.util import raw as raw_util
|
9
9
|
|
10
|
-
class
|
11
|
-
def __init__(self
|
12
|
-
self.slug = slug
|
13
|
-
self.title = title
|
10
|
+
class AbstractPage:
|
11
|
+
def __init__(self):
|
14
12
|
self.elements = []
|
15
|
-
self.add_header(title, level=1) # Add page title as level 1 header
|
16
13
|
|
17
14
|
def add_header(self, text, level=1):
|
18
15
|
if level not in (1, 2, 3, 4):
|
@@ -40,20 +37,25 @@ class Page:
|
|
40
37
|
raise FileNotFoundError(f"File not found: {file_path}")
|
41
38
|
self.elements.append(("download", (file_path, label)))
|
42
39
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
40
|
+
def add_minipage(self, minipage):
|
41
|
+
self.elements.append(("minipage", minipage))
|
42
|
+
|
43
|
+
class Page(AbstractPage):
|
44
|
+
def __init__(self, slug, title):
|
45
|
+
super().__init__()
|
46
|
+
self.slug = slug
|
47
|
+
self.title = title
|
48
|
+
self.add_header(title, level=1)
|
52
49
|
|
53
50
|
def render(self, index):
|
54
|
-
section = div(
|
51
|
+
section = div()
|
52
|
+
minipage_row = []
|
55
53
|
for kind, content in self.elements:
|
56
|
-
if kind == "
|
54
|
+
if kind == "minipage":
|
55
|
+
row_div = div(cls="minipage-row")
|
56
|
+
row_div += content.render(index)
|
57
|
+
section += row_div
|
58
|
+
elif kind == "header":
|
57
59
|
text, level = content
|
58
60
|
if level == 1:
|
59
61
|
section += h1(text)
|
@@ -70,8 +72,65 @@ class Page:
|
|
70
72
|
elif kind == "table":
|
71
73
|
table_html, _ = content
|
72
74
|
section += raw_util(table_html)
|
75
|
+
elif kind == "download":
|
76
|
+
file_path, label = content
|
77
|
+
btn = a(label or os.path.basename(file_path),
|
78
|
+
href=file_path,
|
79
|
+
cls="download-button",
|
80
|
+
download=True)
|
81
|
+
section += div(btn)
|
73
82
|
return section
|
74
83
|
|
84
|
+
class MiniPage(AbstractPage):
|
85
|
+
def __init__(self, width=1.0):
|
86
|
+
super().__init__()
|
87
|
+
self.width = width
|
88
|
+
|
89
|
+
def render(self, index=None):
|
90
|
+
style = f"flex: 0 0 {self.width * 100}%; max-width: {self.width * 100}%;"
|
91
|
+
container = div(cls="minipage", style=style)
|
92
|
+
minipage_row = []
|
93
|
+
for kind, content in self.elements:
|
94
|
+
if kind == "minipage":
|
95
|
+
minipage_row.append(content)
|
96
|
+
else:
|
97
|
+
if minipage_row:
|
98
|
+
row_div = div(cls="minipage-row")
|
99
|
+
for mp in minipage_row:
|
100
|
+
row_div += mp.render(index)
|
101
|
+
container += row_div
|
102
|
+
minipage_row = []
|
103
|
+
if kind == "header":
|
104
|
+
text, level = content
|
105
|
+
if level == 1:
|
106
|
+
container += h1(text)
|
107
|
+
elif level == 2:
|
108
|
+
container += h2(text)
|
109
|
+
elif level == 3:
|
110
|
+
container += h3(text)
|
111
|
+
elif level == 4:
|
112
|
+
container += h4(text)
|
113
|
+
elif kind == "text":
|
114
|
+
container += p(content)
|
115
|
+
elif kind == "plot":
|
116
|
+
container += div(content, cls="plot-container")
|
117
|
+
elif kind == "table":
|
118
|
+
table_html, _ = content
|
119
|
+
container += raw_util(table_html)
|
120
|
+
elif kind == "download":
|
121
|
+
file_path, label = content
|
122
|
+
btn = a(label or os.path.basename(file_path),
|
123
|
+
href=file_path,
|
124
|
+
cls="download-button",
|
125
|
+
download=True)
|
126
|
+
container += div(btn)
|
127
|
+
if minipage_row:
|
128
|
+
row_div = div(cls="minipage-row")
|
129
|
+
for mp in minipage_row:
|
130
|
+
row_div += mp.render(index)
|
131
|
+
container += row_div
|
132
|
+
return container
|
133
|
+
|
75
134
|
class Dashboard:
|
76
135
|
def __init__(self, title="Dashboard"):
|
77
136
|
self.title = title
|
@@ -99,7 +158,6 @@ class Dashboard:
|
|
99
158
|
|
100
159
|
with doc:
|
101
160
|
with div(cls="page-section", id=f"page-{page.slug}"):
|
102
|
-
# Remove h1(page.title) here, since it's already a header element
|
103
161
|
for kind, content in page.elements:
|
104
162
|
if kind == "header":
|
105
163
|
text, level = content
|
@@ -136,39 +194,10 @@ class Dashboard:
|
|
136
194
|
a("Produced by staticdash", href="https://pypi.org/project/staticdash/", target="_blank")
|
137
195
|
|
138
196
|
with div(id="content"):
|
139
|
-
for page in self.pages:
|
140
|
-
with div(id=f"page-{page.slug}", cls="page-section", style="display:none;"):
|
141
|
-
|
142
|
-
|
143
|
-
if kind == "header":
|
144
|
-
text, level = content
|
145
|
-
if level == 1:
|
146
|
-
h1(text)
|
147
|
-
elif level == 2:
|
148
|
-
h2(text)
|
149
|
-
elif level == 3:
|
150
|
-
h3(text)
|
151
|
-
elif level == 4:
|
152
|
-
h4(text)
|
153
|
-
elif kind == "text":
|
154
|
-
p(content)
|
155
|
-
elif kind == "plot":
|
156
|
-
div(content, cls="plot-container")
|
157
|
-
elif kind == "table":
|
158
|
-
table_html, _ = content
|
159
|
-
div(raw_util(table_html))
|
160
|
-
elif kind == "download":
|
161
|
-
src_path, label = content
|
162
|
-
file_uuid = f"{uuid.uuid4().hex}_{os.path.basename(src_path)}"
|
163
|
-
dst_path = os.path.join(downloads_dir, file_uuid)
|
164
|
-
shutil.copy2(src_path, dst_path)
|
165
|
-
download_link = f"downloads/{file_uuid}"
|
166
|
-
btn = a(label or os.path.basename(src_path),
|
167
|
-
href=download_link,
|
168
|
-
cls="download-button",
|
169
|
-
download=True)
|
170
|
-
div(btn)
|
171
|
-
div(raw_util("<br>"))
|
197
|
+
for idx, page in enumerate(self.pages):
|
198
|
+
with div(id=f"page-{page.slug}", cls="page-section", style="display:none;") as section:
|
199
|
+
rendered = page.render(idx)
|
200
|
+
section += rendered # This is correct
|
172
201
|
|
173
202
|
with open(os.path.join(output_dir, "index.html"), "w") as f:
|
174
203
|
f.write(str(index_doc))
|
@@ -0,0 +1,57 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: staticdash
|
3
|
+
Version: 0.3.0
|
4
|
+
Summary: A lightweight static HTML dashboard generator with Plotly and pandas support.
|
5
|
+
Author-email: Brian Day <brian.day1@gmail.com>
|
6
|
+
License: CC0-1.0
|
7
|
+
Project-URL: Homepage, https://github.com/briday1/staticdash
|
8
|
+
Project-URL: Repository, https://github.com/briday1/staticdash
|
9
|
+
Requires-Python: >=3.8
|
10
|
+
Description-Content-Type: text/markdown
|
11
|
+
Requires-Dist: plotly
|
12
|
+
Requires-Dist: pandas
|
13
|
+
Requires-Dist: dominate
|
14
|
+
|
15
|
+
# staticdash
|
16
|
+
|
17
|
+
staticdash is a lightweight Python module for creating static, multi-page HTML dashboards. It supports:
|
18
|
+
|
19
|
+
- Plotly plots (interactive, responsive)
|
20
|
+
- Pandas DataFrames as sortable tables
|
21
|
+
- Text and headers (Markdown-like)
|
22
|
+
- File download buttons
|
23
|
+
- Multi-page navigation with sidebar
|
24
|
+
- Custom CSS and JavaScript
|
25
|
+
- Easy extension for new content types
|
26
|
+
|
27
|
+
## Installation
|
28
|
+
|
29
|
+
```bash
|
30
|
+
git clone https://github.com/briday1/staticdash.git
|
31
|
+
cd staticdash
|
32
|
+
pip install .
|
33
|
+
```
|
34
|
+
|
35
|
+
## Features
|
36
|
+
|
37
|
+
- **Add Plotly figures:** `page.add(fig)`
|
38
|
+
- **Add tables:** `page.add(df)` (sortable by default)
|
39
|
+
- **Add text or headers:** `page.add("Some text")`, `page.add_header("Title", level=2)`
|
40
|
+
- **Add download buttons:** `page.add_download("path/to/file", "Label")`
|
41
|
+
- **Multi-page:** Create multiple `Page` objects and add them to your `Dashboard`
|
42
|
+
- **Custom styling:** Edit `assets/css/style.css` for your own look
|
43
|
+
|
44
|
+
## Options
|
45
|
+
|
46
|
+
- **Sidebar navigation:** Fixed, with active highlighting
|
47
|
+
- **Responsive layout:** Works on desktop and mobile
|
48
|
+
- **Export:** Outputs a static HTML dashboard (no server needed)
|
49
|
+
- **Per-page HTML:** Also generates individual HTML files for each page
|
50
|
+
|
51
|
+
## Live Demo
|
52
|
+
|
53
|
+
[View the latest demo dashboard](https://briday1.github.io/staticdash/)
|
54
|
+
|
55
|
+
---
|
56
|
+
|
57
|
+
For a full example, see [`demo.py`](./demo.py) in this repository.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
staticdash/__init__.py,sha256=KqViaDkiQnhBI8-j3hr14umLDmPgddvdB_G1nJeC5Xs,38
|
2
|
+
staticdash/dashboard.py,sha256=WrUtkwaHxKJ7dCCKuaBtJrP-BqeWmUGpofJWrTH3qJc,8002
|
3
|
+
staticdash/assets/css/style.css,sha256=z3bC-QjAb9f5E3HUkr6R81qQyAfKpMW48yjy11CYH-Q,2779
|
4
|
+
staticdash/assets/js/script.js,sha256=SMOyh7_E_BlvLYMEwYlvCnV7-GnMR-x8PtEiFbIIAsw,3089
|
5
|
+
staticdash-0.3.0.dist-info/METADATA,sha256=DYhxjusTv_YeqNUHmjq37ygzj7C8qZu1eMDTfxLntNg,1776
|
6
|
+
staticdash-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
+
staticdash-0.3.0.dist-info/top_level.txt,sha256=3MzZU6SptkUkjcHV1cvPji0H4aRzPphLHnpStgGEcxM,11
|
8
|
+
staticdash-0.3.0.dist-info/RECORD,,
|
@@ -1,89 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: staticdash
|
3
|
-
Version: 0.2.1
|
4
|
-
Summary: A lightweight static HTML dashboard generator with Plotly and pandas support.
|
5
|
-
Author-email: Brian Day <brian.day1@gmail.com>
|
6
|
-
License: CC0-1.0
|
7
|
-
Project-URL: Homepage, https://github.com/briday1/staticdash
|
8
|
-
Project-URL: Repository, https://github.com/briday1/staticdash
|
9
|
-
Requires-Python: >=3.8
|
10
|
-
Description-Content-Type: text/markdown
|
11
|
-
Requires-Dist: plotly
|
12
|
-
Requires-Dist: pandas
|
13
|
-
Requires-Dist: dominate
|
14
|
-
|
15
|
-
# staticdash
|
16
|
-
|
17
|
-
staticdash is a lightweight Python module for creating static, multi-page HTML dashboards. It supports Plotly plots, tables, and text content, with a fixed sidebar for navigation.
|
18
|
-
|
19
|
-
## Installation
|
20
|
-
|
21
|
-
Clone the repository and install it in editable mode:
|
22
|
-
|
23
|
-
```bash
|
24
|
-
git clone https://github.com/briday1/staticdash.git
|
25
|
-
cd staticdash
|
26
|
-
pip install .
|
27
|
-
```
|
28
|
-
|
29
|
-
## Usage
|
30
|
-
|
31
|
-
Create a Python script like this:
|
32
|
-
|
33
|
-
```python
|
34
|
-
from staticdash.dashboard import Dashboard, Page
|
35
|
-
import plotly.express as px
|
36
|
-
import pandas as pd
|
37
|
-
import numpy as np
|
38
|
-
|
39
|
-
# Create sample data
|
40
|
-
df = pd.DataFrame({
|
41
|
-
"Category": ["A", "B", "C", "D"],
|
42
|
-
"Value": [10, 20, 30, 40]
|
43
|
-
})
|
44
|
-
|
45
|
-
df2 = pd.DataFrame({
|
46
|
-
"Time": pd.date_range("2024-01-01", periods=10, freq="D"),
|
47
|
-
"Signal": np.random.randn(10).cumsum()
|
48
|
-
})
|
49
|
-
|
50
|
-
fig1 = px.bar(df, x="Category", y="Value", title="Bar Chart Example")
|
51
|
-
fig2 = px.line(df2, x="Time", y="Signal", title="Signal over Time")
|
52
|
-
|
53
|
-
# Build dashboard
|
54
|
-
dashboard = Dashboard(title="StaticDash Demo")
|
55
|
-
|
56
|
-
# Page 1: Overview
|
57
|
-
page1 = Page("overview", "Overview")
|
58
|
-
page1.add("Welcome to the StaticDash demo. Below is a bar chart and a table.")
|
59
|
-
page1.add(fig1)
|
60
|
-
page1.add(df)
|
61
|
-
|
62
|
-
# Page 2: Timeseries
|
63
|
-
page2 = Page("timeseries", "Timeseries")
|
64
|
-
page2.add("Here is a random time series with cumulative noise.")
|
65
|
-
page2.add(fig2)
|
66
|
-
page2.add(df2)
|
67
|
-
|
68
|
-
# Page 3: Summary
|
69
|
-
page3 = Page("summary", "Summary")
|
70
|
-
page3.add("Summary and notes can be added here.")
|
71
|
-
page3.add("StaticDash is a lightweight static dashboard generator.")
|
72
|
-
|
73
|
-
# Page 4: Download
|
74
|
-
page4 = Page("download", "Download")
|
75
|
-
page4.add("Here is a button to download a file.")
|
76
|
-
page4.add_download('./test_file.txt', "Download File")
|
77
|
-
page4.add_download('./test_file2.txt', "Download Another File")
|
78
|
-
|
79
|
-
# Register pages
|
80
|
-
dashboard.add_page(page1)
|
81
|
-
dashboard.add_page(page2)
|
82
|
-
dashboard.add_page(page3)
|
83
|
-
dashboard.add_page(page4)
|
84
|
-
|
85
|
-
# Export
|
86
|
-
dashboard.publish(output_dir="output")
|
87
|
-
```
|
88
|
-
|
89
|
-
After running the script, open output/index.html in your browser.
|
@@ -1,8 +0,0 @@
|
|
1
|
-
staticdash/__init__.py,sha256=KqViaDkiQnhBI8-j3hr14umLDmPgddvdB_G1nJeC5Xs,38
|
2
|
-
staticdash/dashboard.py,sha256=trPzco0AZw4IAwZzgY__RsOjwg4UrD7etB-4Da1kHE0,7515
|
3
|
-
staticdash/assets/css/style.css,sha256=kL6FAGxIwYrfNoo4oCzC-ygHmyOZDEjhjixpY0YGpss,2649
|
4
|
-
staticdash/assets/js/script.js,sha256=SMOyh7_E_BlvLYMEwYlvCnV7-GnMR-x8PtEiFbIIAsw,3089
|
5
|
-
staticdash-0.2.1.dist-info/METADATA,sha256=IohPqKSIpXUNPsDhZUFXTpMbtwcrHzYwrhQnUX1Wync,2409
|
6
|
-
staticdash-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
-
staticdash-0.2.1.dist-info/top_level.txt,sha256=3MzZU6SptkUkjcHV1cvPji0H4aRzPphLHnpStgGEcxM,11
|
8
|
-
staticdash-0.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|