staticdash 2025.18__py3-none-any.whl → 2025.19__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/dashboard.py +132 -192
- {staticdash-2025.18.dist-info → staticdash-2025.19.dist-info}/METADATA +1 -1
- staticdash-2025.19.dist-info/RECORD +8 -0
- staticdash-2025.18.dist-info/RECORD +0 -8
- {staticdash-2025.18.dist-info → staticdash-2025.19.dist-info}/WHEEL +0 -0
- {staticdash-2025.18.dist-info → staticdash-2025.19.dist-info}/top_level.txt +0 -0
staticdash/dashboard.py
CHANGED
|
@@ -48,11 +48,12 @@ class AbstractPage:
|
|
|
48
48
|
self.elements.append(("syntax", (code, language), width))
|
|
49
49
|
|
|
50
50
|
class Page(AbstractPage):
|
|
51
|
-
def __init__(self, slug, title, page_width=None):
|
|
51
|
+
def __init__(self, slug, title, page_width=None, marking=None):
|
|
52
52
|
super().__init__()
|
|
53
53
|
self.slug = slug
|
|
54
54
|
self.title = title
|
|
55
55
|
self.page_width = page_width
|
|
56
|
+
self.marking = marking # Page-specific marking
|
|
56
57
|
self.children = []
|
|
57
58
|
self.add_header(title, level=1)
|
|
58
59
|
|
|
@@ -62,6 +63,20 @@ class Page(AbstractPage):
|
|
|
62
63
|
def render(self, index, downloads_dir=None, relative_prefix="", inherited_width=None):
|
|
63
64
|
effective_width = self.page_width or inherited_width
|
|
64
65
|
elements = []
|
|
66
|
+
|
|
67
|
+
# Add floating header and footer for marking
|
|
68
|
+
marking = self.marking or "Default Marking"
|
|
69
|
+
elements.append(div(
|
|
70
|
+
marking,
|
|
71
|
+
cls="floating-header",
|
|
72
|
+
style="position: fixed; top: 0; left: 50%; transform: translateX(-50%); width: auto; background-color: #f8f9fa; text-align: center; padding: 10px; z-index: 1000; font-weight: normal;"
|
|
73
|
+
))
|
|
74
|
+
elements.append(div(
|
|
75
|
+
marking,
|
|
76
|
+
cls="floating-footer",
|
|
77
|
+
style="position: fixed; bottom: 0; left: 50%; transform: translateX(-50%); width: auto; background-color: #f8f9fa; text-align: center; padding: 10px; z-index: 1000; font-weight: normal;"
|
|
78
|
+
))
|
|
79
|
+
|
|
65
80
|
for kind, content, el_width in self.elements:
|
|
66
81
|
style = ""
|
|
67
82
|
outer_style = ""
|
|
@@ -77,10 +92,8 @@ class Page(AbstractPage):
|
|
|
77
92
|
elem = header_tag(text)
|
|
78
93
|
elif kind == "plot":
|
|
79
94
|
fig = content
|
|
80
|
-
# Plotly support (existing)
|
|
81
95
|
if hasattr(fig, "to_html"):
|
|
82
96
|
elem = div(raw_util(fig.to_html(full_html=False, include_plotlyjs='cdn', config={'responsive': True})))
|
|
83
|
-
# Matplotlib support
|
|
84
97
|
else:
|
|
85
98
|
try:
|
|
86
99
|
buf = io.BytesIO()
|
|
@@ -88,7 +101,6 @@ class Page(AbstractPage):
|
|
|
88
101
|
buf.seek(0)
|
|
89
102
|
img_base64 = base64.b64encode(buf.read()).decode("utf-8")
|
|
90
103
|
buf.close()
|
|
91
|
-
# Center the image using a div with inline styles
|
|
92
104
|
elem = div(
|
|
93
105
|
raw_util(f'<img src="data:image/png;base64,{img_base64}" style="max-width:100%;">'),
|
|
94
106
|
style="display: flex; justify-content: center; align-items: center;"
|
|
@@ -97,8 +109,11 @@ class Page(AbstractPage):
|
|
|
97
109
|
elem = div(f"Matplotlib figure could not be rendered: {e}")
|
|
98
110
|
elif kind == "table":
|
|
99
111
|
df = content
|
|
100
|
-
|
|
101
|
-
|
|
112
|
+
try:
|
|
113
|
+
html_table = df.to_html(classes="table-hover table-striped", index=False, border=0, table_id=f"table-{index}", escape=False)
|
|
114
|
+
elem = div(raw_util(html_table))
|
|
115
|
+
except Exception as e:
|
|
116
|
+
elem = div(f"Table could not be rendered: {e}")
|
|
102
117
|
elif kind == "download":
|
|
103
118
|
file_path, label = content
|
|
104
119
|
btn = a(label or os.path.basename(file_path), href=file_path, cls="download-button", download=True)
|
|
@@ -117,7 +132,9 @@ class Page(AbstractPage):
|
|
|
117
132
|
elem = div(elem, style=style)
|
|
118
133
|
elem = div(elem, style=outer_style)
|
|
119
134
|
elements.append(elem)
|
|
120
|
-
|
|
135
|
+
|
|
136
|
+
# Add padding to avoid overlap with header and footer
|
|
137
|
+
wrapper = div(*elements, style=f"max-width: {effective_width}px; margin: 0 auto; width: 100%; padding-top: 80px; padding-bottom: 80px;")
|
|
121
138
|
return [wrapper]
|
|
122
139
|
|
|
123
140
|
class MiniPage(AbstractPage):
|
|
@@ -187,10 +204,11 @@ class MiniPage(AbstractPage):
|
|
|
187
204
|
return row_div
|
|
188
205
|
|
|
189
206
|
class Dashboard:
|
|
190
|
-
def __init__(self, title="Dashboard", page_width=900):
|
|
207
|
+
def __init__(self, title="Dashboard", page_width=900, marking=None):
|
|
191
208
|
self.title = title
|
|
192
209
|
self.pages = []
|
|
193
210
|
self.page_width = page_width
|
|
211
|
+
self.marking = marking # Dashboard-wide marking
|
|
194
212
|
|
|
195
213
|
def add_page(self, page):
|
|
196
214
|
self.pages.append(page)
|
|
@@ -298,202 +316,124 @@ class Dashboard:
|
|
|
298
316
|
with open(os.path.join(output_dir, "index.html"), "w") as f:
|
|
299
317
|
f.write(str(index_doc))
|
|
300
318
|
|
|
301
|
-
def publish_pdf(self, output_path="dashboard_report.pdf", pagesize="A4",
|
|
302
|
-
from reportlab.
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
319
|
+
def publish_pdf(self, output_path="dashboard_report.pdf", pagesize="A4", include_title_page=False, title_page_marking=None, author=None, affiliation=None):
|
|
320
|
+
from reportlab.platypus import SimpleDocTemplate, Spacer, Paragraph, PageBreak, Image
|
|
321
|
+
from reportlab.lib.pagesizes import letter, A4
|
|
322
|
+
from reportlab.lib.styles import getSampleStyleSheet
|
|
323
|
+
from datetime import datetime
|
|
324
|
+
import io
|
|
307
325
|
|
|
326
|
+
page_size = A4 if pagesize == "A4" else letter
|
|
308
327
|
styles = getSampleStyleSheet()
|
|
309
|
-
styles['Heading1'].fontSize = 18
|
|
310
|
-
styles['Heading1'].leading = 22
|
|
311
|
-
styles['Heading1'].spaceAfter = 12
|
|
312
|
-
styles['Heading1'].spaceBefore = 18
|
|
313
|
-
styles['Heading1'].fontName = 'Helvetica-Bold'
|
|
314
|
-
styles['Heading2'].fontSize = 14
|
|
315
|
-
styles['Heading2'].leading = 18
|
|
316
|
-
styles['Heading2'].spaceAfter = 8
|
|
317
|
-
styles['Heading2'].spaceBefore = 12
|
|
318
|
-
styles['Heading2'].fontName = 'Helvetica-Bold'
|
|
319
|
-
if 'CodeBlock' not in styles:
|
|
320
|
-
styles.add(ParagraphStyle(name='CodeBlock', fontName='Courier', fontSize=9, leading=12, backColor=colors.whitesmoke, leftIndent=12, rightIndent=12, spaceAfter=8, borderPadding=4))
|
|
321
|
-
normal_style = styles['Normal']
|
|
322
|
-
|
|
323
328
|
story = []
|
|
324
|
-
outline_entries = []
|
|
325
|
-
heading_paragraphs = []
|
|
326
|
-
|
|
327
|
-
class MyDocTemplate(SimpleDocTemplate):
|
|
328
|
-
def __init__(self, *args, outline_entries=None, headings=None, **kwargs):
|
|
329
|
-
super().__init__(*args, **kwargs)
|
|
330
|
-
self.outline_entries = outline_entries or []
|
|
331
|
-
self.headings = headings or []
|
|
332
|
-
self._outline_idx = 0
|
|
333
|
-
|
|
334
|
-
def afterFlowable(self, flowable):
|
|
335
|
-
if hasattr(flowable, 'getPlainText'):
|
|
336
|
-
text = flowable.getPlainText().strip()
|
|
337
|
-
if self._outline_idx < len(self.outline_entries):
|
|
338
|
-
expected_title, level, section_num = self.outline_entries[self._outline_idx]
|
|
339
|
-
expected = expected_title.strip()
|
|
340
|
-
if text == expected:
|
|
341
|
-
bookmark_name = f"section_{section_num.replace('.', '_')}"
|
|
342
|
-
self.canv.bookmarkPage(bookmark_name)
|
|
343
|
-
self.canv.addOutlineEntry(expected_title, bookmark_name, level=level, closed=False)
|
|
344
|
-
self._outline_idx += 1
|
|
345
|
-
|
|
346
|
-
def render_page(page, level=0, sec_prefix=[]):
|
|
347
|
-
if len(sec_prefix) <= level:
|
|
348
|
-
sec_prefix.append(1)
|
|
349
|
-
else:
|
|
350
|
-
sec_prefix[level] += 1
|
|
351
|
-
sec_prefix = sec_prefix[:level+1]
|
|
352
|
-
section_num = ".".join(str(n) for n in sec_prefix)
|
|
353
|
-
section_title = f"{section_num} {page.title}"
|
|
354
|
-
outline_entries.append((section_title, level, section_num))
|
|
355
|
-
style = styles['Heading1'] if level == 0 else styles['Heading2']
|
|
356
|
-
bookmark_name = f"section_{section_num.replace('.', '_')}"
|
|
357
|
-
para = Paragraph(f'<a name="{bookmark_name}"/>{section_title}', style)
|
|
358
|
-
heading_paragraphs.append(para)
|
|
359
|
-
story.append(para)
|
|
360
|
-
story.append(Spacer(1, 12))
|
|
361
|
-
|
|
362
|
-
def render_elements(elements):
|
|
363
|
-
for kind, content, _ in elements:
|
|
364
|
-
if kind == "text":
|
|
365
|
-
story.append(Paragraph(content, normal_style))
|
|
366
|
-
story.append(Spacer(1, 8))
|
|
367
|
-
elif kind == "header":
|
|
368
|
-
text, level_ = content
|
|
369
|
-
header_style = styles['Heading{}'.format(min(level_+1, 4))]
|
|
370
|
-
story.append(Paragraph(text, header_style))
|
|
371
|
-
story.append(Spacer(1, 8))
|
|
372
|
-
elif kind == "table":
|
|
373
|
-
df = content
|
|
374
|
-
try:
|
|
375
|
-
data = [df.columns.tolist()] + df.values.tolist()
|
|
376
|
-
t = Table(data, repeatRows=1)
|
|
377
|
-
t.setStyle(TableStyle([
|
|
378
|
-
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#222C36")),
|
|
379
|
-
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
|
|
380
|
-
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
|
381
|
-
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
|
382
|
-
('FONTSIZE', (0, 0), (-1, 0), 11),
|
|
383
|
-
('BOTTOMPADDING', (0, 0), (-1, 0), 10),
|
|
384
|
-
('TOPPADDING', (0, 0), (-1, 0), 10),
|
|
385
|
-
('BACKGROUND', (0, 1), (-1, -1), colors.whitesmoke),
|
|
386
|
-
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#B0B8C1")),
|
|
387
|
-
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
|
|
388
|
-
('FONTSIZE', (0, 1), (-1, -1), 10),
|
|
389
|
-
('LEFTPADDING', (0, 0), (-1, -1), 6),
|
|
390
|
-
('RIGHTPADDING', (0, 0), (-1, -1), 6),
|
|
391
|
-
('TOPPADDING', (0, 1), (-1, -1), 6),
|
|
392
|
-
('BOTTOMPADDING', (0, 1), (-1, -1), 6),
|
|
393
|
-
]))
|
|
394
|
-
story.append(t)
|
|
395
|
-
story.append(Spacer(1, 12))
|
|
396
|
-
except Exception:
|
|
397
|
-
story.append(Paragraph("Table could not be rendered.", normal_style))
|
|
398
|
-
elif kind == "plot":
|
|
399
|
-
fig = content
|
|
400
|
-
try:
|
|
401
|
-
import plotly.graph_objects as go
|
|
402
|
-
import matplotlib.figure
|
|
403
|
-
import io
|
|
404
|
-
from reportlab.platypus import Image
|
|
405
|
-
|
|
406
|
-
# Plotly support
|
|
407
|
-
if isinstance(fig, go.Figure):
|
|
408
|
-
# Configure the figure layout for PDF rendering
|
|
409
|
-
fig.update_layout(
|
|
410
|
-
margin=dict(l=10, r=10, t=30, b=30),
|
|
411
|
-
width=900,
|
|
412
|
-
height=540
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
# Use kaleido to export the figure as a PNG
|
|
416
|
-
png_bytes = fig.to_image(format="png", width=900, height=540, engine="kaleido")
|
|
417
|
-
|
|
418
|
-
# Wrap the PNG bytes in a BytesIO buffer
|
|
419
|
-
img_buf = io.BytesIO(png_bytes)
|
|
420
|
-
|
|
421
|
-
# Add the image to the PDF story
|
|
422
|
-
story.append(Spacer(1, 8))
|
|
423
|
-
story.append(Image(img_buf, width=6 * inch, height=3.6 * inch))
|
|
424
|
-
story.append(Spacer(1, 12))
|
|
425
|
-
|
|
426
|
-
# Matplotlib support
|
|
427
|
-
elif isinstance(fig, matplotlib.figure.Figure):
|
|
428
|
-
buf = io.BytesIO()
|
|
429
|
-
# Save the figure with higher DPI for better quality
|
|
430
|
-
fig.savefig(buf, format="png", bbox_inches="tight", dpi=300)
|
|
431
|
-
buf.seek(0)
|
|
432
|
-
|
|
433
|
-
# Calculate aspect ratio
|
|
434
|
-
fig_width, fig_height = fig.get_size_inches()
|
|
435
|
-
aspect_ratio = fig_height / fig_width
|
|
436
|
-
|
|
437
|
-
# Set width and calculate height based on aspect ratio
|
|
438
|
-
pdf_width = 6 * inch
|
|
439
|
-
pdf_height = pdf_width * aspect_ratio
|
|
440
|
-
|
|
441
|
-
# Add the image to the PDF story
|
|
442
|
-
story.append(Spacer(1, 8))
|
|
443
|
-
story.append(Image(buf, width=pdf_width, height=pdf_height))
|
|
444
|
-
story.append(Spacer(1, 12))
|
|
445
|
-
|
|
446
|
-
else:
|
|
447
|
-
raise ValueError("add_plot must be called with a plotly.graph_objects.Figure or matplotlib.figure.Figure")
|
|
448
|
-
|
|
449
|
-
except Exception as e:
|
|
450
|
-
story.append(Paragraph(f"Plot rendering not supported in PDF: {e}", normal_style))
|
|
451
|
-
elif kind == "syntax":
|
|
452
|
-
code, language = content
|
|
453
|
-
story.append(Paragraph(f"<b>Code ({language}):</b>", normal_style))
|
|
454
|
-
story.append(Spacer(1, 4))
|
|
455
|
-
code_html = html.escape(code).replace(' ', ' ').replace('\n', '<br/>')
|
|
456
|
-
story.append(Paragraph(f"<font face='Courier'>{code_html}</font>", styles['CodeBlock']))
|
|
457
|
-
story.append(Spacer(1, 12))
|
|
458
|
-
elif kind == "minipage":
|
|
459
|
-
render_elements(content.elements)
|
|
460
|
-
|
|
461
|
-
render_elements(page.elements)
|
|
462
|
-
|
|
463
|
-
for i, child in enumerate(getattr(page, "children", []), start=0):
|
|
464
|
-
story.append(PageBreak())
|
|
465
|
-
child_sec_prefix = sec_prefix.copy() + [i]
|
|
466
|
-
render_page(child, level=level+1, sec_prefix=child_sec_prefix)
|
|
467
|
-
|
|
468
|
-
if level == 0:
|
|
469
|
-
story.append(PageBreak())
|
|
470
329
|
|
|
330
|
+
# Add title page
|
|
471
331
|
if include_title_page:
|
|
472
332
|
story.append(Spacer(1, 120))
|
|
473
|
-
# Title, centered and bold
|
|
474
333
|
story.append(Paragraph(f"<b>{self.title}</b>", styles['Title']))
|
|
475
334
|
story.append(Spacer(1, 48))
|
|
476
|
-
|
|
477
|
-
|
|
335
|
+
|
|
336
|
+
# Center author, affiliation, and date
|
|
478
337
|
if author:
|
|
479
|
-
|
|
338
|
+
story.append(Paragraph(f"<para align='center'>{author}</para>", styles['Normal']))
|
|
480
339
|
if affiliation:
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
340
|
+
story.append(Paragraph(f"<para align='center'>{affiliation}</para>", styles['Normal']))
|
|
341
|
+
current_date = datetime.now().strftime("%B %d, %Y")
|
|
342
|
+
story.append(Paragraph(f"<para align='center'>{current_date}</para>", styles['Normal']))
|
|
343
|
+
|
|
485
344
|
story.append(PageBreak())
|
|
486
345
|
|
|
346
|
+
# Add markings to the PDF
|
|
347
|
+
def add_marking(canvas, doc, marking):
|
|
348
|
+
if marking:
|
|
349
|
+
canvas.saveState()
|
|
350
|
+
canvas.setFont("Helvetica", 10)
|
|
351
|
+
page_width = doc.pagesize[0]
|
|
352
|
+
text_width = canvas.stringWidth(marking, "Helvetica", 10)
|
|
353
|
+
x_position = (page_width - text_width) / 2 # Center the marking
|
|
354
|
+
canvas.drawString(x_position, doc.pagesize[1] - 36, marking) # Header
|
|
355
|
+
canvas.drawString(x_position, 36, marking) # Footer
|
|
356
|
+
canvas.restoreState()
|
|
357
|
+
|
|
358
|
+
# Recursive function to render pages and subpages
|
|
359
|
+
def render_page(page):
|
|
360
|
+
# Render the current page
|
|
361
|
+
for kind, content, _ in page.elements:
|
|
362
|
+
if kind == "text":
|
|
363
|
+
story.append(Paragraph(content, styles['Normal']))
|
|
364
|
+
story.append(Spacer(1, 8))
|
|
365
|
+
elif kind == "header":
|
|
366
|
+
text, level = content
|
|
367
|
+
header_style = styles[f'Heading{min(level + 1, 4)}']
|
|
368
|
+
story.append(Paragraph(text, header_style))
|
|
369
|
+
story.append(Spacer(1, 8))
|
|
370
|
+
elif kind == "table":
|
|
371
|
+
df = content
|
|
372
|
+
try:
|
|
373
|
+
data = [df.columns.tolist()] + df.values.tolist()
|
|
374
|
+
t = Table(data, repeatRows=1)
|
|
375
|
+
t.setStyle(TableStyle([
|
|
376
|
+
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#222C36")),
|
|
377
|
+
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
|
|
378
|
+
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
|
379
|
+
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
|
380
|
+
('FONTSIZE', (0, 0), (-1, 0), 11),
|
|
381
|
+
('BOTTOMPADDING', (0, 0), (-1, 0), 10),
|
|
382
|
+
('TOPPADDING', (0, 0), (-1, 0), 10),
|
|
383
|
+
('BACKGROUND', (0, 1), (-1, -1), colors.whitesmoke),
|
|
384
|
+
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#B0B8C1")),
|
|
385
|
+
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
|
|
386
|
+
('FONTSIZE', (0, 1), (-1, -1), 10),
|
|
387
|
+
('LEFTPADDING', (0, 0), (-1, -1), 6),
|
|
388
|
+
('RIGHTPADDING', (0, 0), (-1, -1), 6),
|
|
389
|
+
('TOPPADDING', (0, 1), (-1, -1), 6),
|
|
390
|
+
('BOTTOMPADDING', (0, 1), (-1, -1), 6),
|
|
391
|
+
]))
|
|
392
|
+
story.append(t)
|
|
393
|
+
story.append(Spacer(1, 12))
|
|
394
|
+
except Exception:
|
|
395
|
+
story.append(Paragraph("Table could not be rendered.", styles['Normal']))
|
|
396
|
+
elif kind == "plot":
|
|
397
|
+
fig = content
|
|
398
|
+
try:
|
|
399
|
+
# Handle Plotly figures
|
|
400
|
+
if hasattr(fig, "to_image"):
|
|
401
|
+
img_bytes = fig.to_image(format="png", width=800, height=600, scale=2)
|
|
402
|
+
img_buffer = io.BytesIO(img_bytes)
|
|
403
|
+
img = Image(img_buffer, width=6 * inch, height=4.5 * inch)
|
|
404
|
+
story.append(img)
|
|
405
|
+
story.append(Spacer(1, 12))
|
|
406
|
+
# Handle Matplotlib figures
|
|
407
|
+
elif hasattr(fig, "savefig"):
|
|
408
|
+
buf = io.BytesIO()
|
|
409
|
+
fig.savefig(buf, format="png", bbox_inches="tight", dpi=300)
|
|
410
|
+
buf.seek(0)
|
|
411
|
+
img = Image(buf, width=6 * inch, height=4.5 * inch)
|
|
412
|
+
story.append(img)
|
|
413
|
+
story.append(Spacer(1, 12))
|
|
414
|
+
except Exception as e:
|
|
415
|
+
story.append(Paragraph(f"Plot could not be rendered: {e}", styles['Normal']))
|
|
416
|
+
elif kind == "syntax":
|
|
417
|
+
# Handle syntax blocks
|
|
418
|
+
pass
|
|
419
|
+
elif kind == "minipage":
|
|
420
|
+
# Handle subpages
|
|
421
|
+
pass
|
|
422
|
+
|
|
423
|
+
# Recursively render subpages
|
|
424
|
+
for child in getattr(page, "children", []):
|
|
425
|
+
story.append(PageBreak()) # Add a PageBreak before rendering subpages
|
|
426
|
+
render_page(child)
|
|
427
|
+
|
|
428
|
+
# Render all pages
|
|
487
429
|
for page in self.pages:
|
|
488
430
|
render_page(page)
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
431
|
+
story.append(PageBreak()) # Add a PageBreak after each top-level page
|
|
432
|
+
|
|
433
|
+
# Build the PDF
|
|
434
|
+
doc = SimpleDocTemplate(output_path, pagesize=page_size)
|
|
435
|
+
doc.build(
|
|
436
|
+
story,
|
|
437
|
+
onFirstPage=lambda canvas, doc: add_marking(canvas, doc, title_page_marking),
|
|
438
|
+
onLaterPages=lambda canvas, doc: add_marking(canvas, doc, self.marking)
|
|
497
439
|
)
|
|
498
|
-
|
|
499
|
-
doc.multiBuild(story)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
staticdash/__init__.py,sha256=UN_-h8wFGfTPHYjnEb7N9CsxqXo-DQVo0cmREOtvRXE,244
|
|
2
|
+
staticdash/dashboard.py,sha256=fTxesbZOkM4H2VEDZX152z8gLLZ3w0ahdYA7yBJ08aA,21960
|
|
3
|
+
staticdash/assets/css/style.css,sha256=RVqNdwBsaDv8izdOQjGmUZ4NROWF8uZhiq8DTNvUB1M,5962
|
|
4
|
+
staticdash/assets/js/script.js,sha256=7xBRlz_19wybbNVwAcfuKNXtDEojGB4EB0Yj4klsoTA,6998
|
|
5
|
+
staticdash-2025.19.dist-info/METADATA,sha256=Mmt-_2QcTkiWSV6nXRUZhuNBbMchCom9UC3lW45aG70,1960
|
|
6
|
+
staticdash-2025.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
staticdash-2025.19.dist-info/top_level.txt,sha256=3MzZU6SptkUkjcHV1cvPji0H4aRzPphLHnpStgGEcxM,11
|
|
8
|
+
staticdash-2025.19.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
staticdash/__init__.py,sha256=UN_-h8wFGfTPHYjnEb7N9CsxqXo-DQVo0cmREOtvRXE,244
|
|
2
|
-
staticdash/dashboard.py,sha256=6d3TbXVN0ZdWD5awsqWH9H6tH64fpOanTAiVWG_q-Eo,25056
|
|
3
|
-
staticdash/assets/css/style.css,sha256=RVqNdwBsaDv8izdOQjGmUZ4NROWF8uZhiq8DTNvUB1M,5962
|
|
4
|
-
staticdash/assets/js/script.js,sha256=7xBRlz_19wybbNVwAcfuKNXtDEojGB4EB0Yj4klsoTA,6998
|
|
5
|
-
staticdash-2025.18.dist-info/METADATA,sha256=_Zrirh0NBbV36fcXJQ4oqGQ_lr-UPhYSwLr6Vy3ur3U,1960
|
|
6
|
-
staticdash-2025.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
-
staticdash-2025.18.dist-info/top_level.txt,sha256=3MzZU6SptkUkjcHV1cvPji0H4aRzPphLHnpStgGEcxM,11
|
|
8
|
-
staticdash-2025.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|