staticdash 2025.22__tar.gz → 2025.24__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.
- {staticdash-2025.22 → staticdash-2025.24}/PKG-INFO +1 -1
- {staticdash-2025.22 → staticdash-2025.24}/pyproject.toml +1 -1
- {staticdash-2025.22 → staticdash-2025.24}/staticdash/dashboard.py +68 -54
- {staticdash-2025.22 → staticdash-2025.24}/staticdash.egg-info/PKG-INFO +1 -1
- {staticdash-2025.22 → staticdash-2025.24}/README.md +0 -0
- {staticdash-2025.22 → staticdash-2025.24}/setup.cfg +0 -0
- {staticdash-2025.22 → staticdash-2025.24}/staticdash/__init__.py +0 -0
- {staticdash-2025.22 → staticdash-2025.24}/staticdash/assets/css/style.css +0 -0
- {staticdash-2025.22 → staticdash-2025.24}/staticdash/assets/js/script.js +0 -0
- {staticdash-2025.22 → staticdash-2025.24}/staticdash.egg-info/SOURCES.txt +0 -0
- {staticdash-2025.22 → staticdash-2025.24}/staticdash.egg-info/dependency_links.txt +0 -0
- {staticdash-2025.22 → staticdash-2025.24}/staticdash.egg-info/requires.txt +0 -0
- {staticdash-2025.22 → staticdash-2025.24}/staticdash.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "staticdash"
|
|
7
|
-
version = "2025.
|
|
7
|
+
version = "2025.24"
|
|
8
8
|
description = "A lightweight static HTML dashboard generator with Plotly and pandas support."
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Brian Day", email = "brian.day1@gmail.com" }
|
|
@@ -427,6 +427,7 @@ class Dashboard:
|
|
|
427
427
|
self.canv.bookmarkPage(key)
|
|
428
428
|
self.canv.addOutlineEntry(text, key, level=outline_level, closed=False)
|
|
429
429
|
|
|
430
|
+
|
|
430
431
|
self.notify('TOCEntry', (outline_level, text, self.page))
|
|
431
432
|
|
|
432
433
|
|
|
@@ -466,28 +467,29 @@ class Dashboard:
|
|
|
466
467
|
story.append(PageBreak())
|
|
467
468
|
|
|
468
469
|
def render_page(page, level=0, sec_prefix=[]):
|
|
469
|
-
heading_style = styles
|
|
470
|
+
heading_style = styles.get(f'Heading{min(level + 1, 4)}', styles['Heading4'])
|
|
471
|
+
|
|
472
|
+
# Remember where we started
|
|
473
|
+
content_start = len(story)
|
|
470
474
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
story.append(Paragraph(page.title, heading_style))
|
|
474
|
-
story.append(Spacer(1, 12))
|
|
475
|
+
story.append(Paragraph(page.title, heading_style))
|
|
476
|
+
story.append(Spacer(1, 12))
|
|
475
477
|
|
|
476
478
|
for kind, content, _ in page.elements:
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
479
|
+
try:
|
|
480
|
+
if kind == "text":
|
|
481
|
+
story.append(Paragraph(content, normal_style))
|
|
482
|
+
story.append(Spacer(1, 8))
|
|
483
|
+
|
|
484
|
+
elif kind == "header":
|
|
485
|
+
text, lvl = content
|
|
486
|
+
safe_lvl = max(1, min(lvl + 1, 4))
|
|
487
|
+
style = styles[f'Heading{safe_lvl}']
|
|
488
|
+
story.append(Paragraph(text, style))
|
|
489
|
+
story.append(Spacer(1, 8))
|
|
490
|
+
|
|
491
|
+
elif kind == "table":
|
|
492
|
+
df = content
|
|
491
493
|
data = [df.columns.tolist()] + df.values.tolist()
|
|
492
494
|
t = Table(data, repeatRows=1)
|
|
493
495
|
t.setStyle(TableStyle([
|
|
@@ -509,47 +511,59 @@ class Dashboard:
|
|
|
509
511
|
]))
|
|
510
512
|
story.append(t)
|
|
511
513
|
story.append(Spacer(1, 12))
|
|
512
|
-
except Exception:
|
|
513
|
-
story.append(Paragraph("Table could not be rendered.", normal_style))
|
|
514
514
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if hasattr(fig, "savefig"):
|
|
519
|
-
buf = io.BytesIO()
|
|
515
|
+
elif kind == "plot":
|
|
516
|
+
fig = content
|
|
517
|
+
buf = io.BytesIO()
|
|
518
|
+
if hasattr(fig, "savefig"):
|
|
520
519
|
fig.savefig(buf, format="png", bbox_inches="tight", dpi=300)
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmpfile:
|
|
527
|
-
fig.write_image(tmpfile.name, width=600, height=360, scale=2)
|
|
528
|
-
with open(tmpfile.name, "rb") as f:
|
|
529
|
-
story.append(Spacer(1, 8))
|
|
530
|
-
story.append(Image(io.BytesIO(f.read()), width=6*inch, height=3.6*inch))
|
|
531
|
-
story.append(Spacer(1, 12))
|
|
532
|
-
os.unlink(tmpfile.name)
|
|
533
|
-
except Exception as e:
|
|
534
|
-
story.append(Paragraph(f"Plot rendering failed: {e}", normal_style))
|
|
520
|
+
else:
|
|
521
|
+
fig.write_image(buf, format="png", width=600, height=360, scale=2)
|
|
522
|
+
buf.seek(0)
|
|
523
|
+
story.append(Image(buf, width=6 * inch, height=4.5 * inch))
|
|
524
|
+
story.append(Spacer(1, 12))
|
|
535
525
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
526
|
+
elif kind == "syntax":
|
|
527
|
+
code, language = content
|
|
528
|
+
from html import escape
|
|
529
|
+
story.append(Paragraph(f"<b>Code ({language}):</b>", normal_style))
|
|
530
|
+
story.append(Spacer(1, 4))
|
|
531
|
+
code_html = escape(code).replace(" ", " ").replace("\n", "<br/>")
|
|
532
|
+
story.append(Paragraph(f"<font face='Courier'>{code_html}</font>", styles['CodeBlock']))
|
|
533
|
+
story.append(Spacer(1, 12))
|
|
544
534
|
|
|
545
|
-
|
|
546
|
-
|
|
535
|
+
elif kind == "minipage":
|
|
536
|
+
render_page(content, level=level + 1, sec_prefix=sec_prefix)
|
|
537
|
+
|
|
538
|
+
except Exception as e:
|
|
539
|
+
story.append(Paragraph(f"Error rendering element: {e}", normal_style))
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
just_broke = False
|
|
543
|
+
|
|
544
|
+
for i, child in enumerate(page.children):
|
|
545
|
+
if i > 0 and not just_broke:
|
|
546
|
+
story.append(PageBreak())
|
|
547
|
+
|
|
548
|
+
before = len(story)
|
|
549
|
+
render_page(child, level=level + 1, sec_prefix=sec_prefix + [i + 1])
|
|
550
|
+
after = len(story)
|
|
551
|
+
|
|
552
|
+
# Determine if child added a PageBreak
|
|
553
|
+
just_broke = isinstance(story[-1], PageBreak) if after > before else False
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
# Determine if anything meaningful was added
|
|
557
|
+
def has_meaningful_content(start_idx):
|
|
558
|
+
for elem in story[start_idx:]:
|
|
559
|
+
if isinstance(elem, (Paragraph, Table, Image)):
|
|
560
|
+
return True
|
|
561
|
+
return False
|
|
562
|
+
|
|
563
|
+
if not page.children and has_meaningful_content(content_start):
|
|
564
|
+
story.append(PageBreak())
|
|
547
565
|
|
|
548
|
-
# for child in getattr(page, "children", []):
|
|
549
|
-
# story.append(PageBreak())
|
|
550
|
-
# render_page(child, level=level + 2, sec_prefix=sec_prefix + [1])
|
|
551
566
|
|
|
552
|
-
story.append(PageBreak())
|
|
553
567
|
|
|
554
568
|
for page in self.pages:
|
|
555
569
|
render_page(page)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|