Files

118 lines
5.4 KiB
Python

from pathlib import Path
import re, html
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Preformatted, ListFlowable, ListItem
from reportlab.lib.units import inch
base = Path.home()/'.timmy'/'reports'/'notebooklm'/'2026-03-27-hermes-openclaw'
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name='Body2', parent=styles['BodyText'], fontName='Helvetica', fontSize=10.5, leading=14, spaceAfter=6))
styles.add(ParagraphStyle(name='H1x', parent=styles['Heading1'], fontName='Helvetica-Bold', fontSize=20, leading=24, spaceAfter=12, textColor=colors.HexColor('#111111')))
styles.add(ParagraphStyle(name='H2x', parent=styles['Heading2'], fontName='Helvetica-Bold', fontSize=15, leading=19, spaceBefore=8, spaceAfter=8, textColor=colors.HexColor('#222222')))
styles.add(ParagraphStyle(name='H3x', parent=styles['Heading3'], fontName='Helvetica-Bold', fontSize=12.5, leading=16, spaceBefore=6, spaceAfter=6))
styles.add(ParagraphStyle(name='CodeBlockX', parent=styles['Code'], fontName='Courier', fontSize=8.5, leading=10.5, leftIndent=12, rightIndent=12, backColor=colors.HexColor('#F4F4F4'), borderPadding=6, spaceAfter=8))
styles.add(ParagraphStyle(name='MetaX', parent=styles['BodyText'], fontName='Helvetica-Oblique', fontSize=9.5, leading=12, textColor=colors.HexColor('#555555'), spaceAfter=6))
url_re = re.compile(r'(https?://[^\s)]+)')
def linkify(text: str) -> str:
esc = html.escape(text)
return url_re.sub(lambda m: f'<link href="{m.group(1)}" color="blue">{m.group(1)}</link>', esc)
def markdown_to_story(md_path: Path):
text = md_path.read_text()
lines = text.splitlines()
story = []
i = 0
while i < len(lines):
line = lines[i]
if not line.strip():
i += 1
continue
if line.startswith('```'):
i += 1
buf = []
while i < len(lines) and not lines[i].startswith('```'):
buf.append(lines[i])
i += 1
if i < len(lines) and lines[i].startswith('```'):
i += 1
story.append(Preformatted('\n'.join(buf), styles['CodeBlockX']))
continue
if line.startswith('# '):
story.append(Paragraph(linkify(line[2:].strip()), styles['H1x']))
i += 1
continue
if line.startswith('## '):
story.append(Paragraph(linkify(line[3:].strip()), styles['H2x']))
i += 1
continue
if line.startswith('### '):
story.append(Paragraph(linkify(line[4:].strip()), styles['H3x']))
i += 1
continue
if re.match(r'^-\s+', line):
items = []
while i < len(lines) and re.match(r'^-\s+', lines[i]):
item_text = re.sub(r'^-\s+', '', lines[i]).strip()
items.append(ListItem(Paragraph(linkify(item_text), styles['Body2'])))
i += 1
story.append(ListFlowable(items, bulletType='bullet', leftIndent=18))
story.append(Spacer(1, 6))
continue
if re.match(r'^\d+\.\s+', line):
items = []
while i < len(lines) and re.match(r'^\d+\.\s+', lines[i]):
item_text = re.sub(r'^\d+\.\s+', '', lines[i]).strip()
items.append(ListItem(Paragraph(linkify(item_text), styles['Body2'])))
i += 1
story.append(ListFlowable(items, bulletType='1', leftIndent=18))
story.append(Spacer(1, 6))
continue
buf = [line.strip()]
i += 1
while i < len(lines) and lines[i].strip() and not any([
lines[i].startswith('# '), lines[i].startswith('## '), lines[i].startswith('### '),
lines[i].startswith('```'), re.match(r'^-\s+', lines[i]), re.match(r'^\d+\.\s+', lines[i])
]):
buf.append(lines[i].strip())
i += 1
paragraph = ' '.join(buf)
style = styles['MetaX'] if paragraph.startswith('Date:') or paragraph.startswith('Audience:') or paragraph.startswith('Subject:') else styles['Body2']
story.append(Paragraph(linkify(paragraph), style))
return story
def add_footer(canvas, doc):
canvas.saveState()
canvas.setFont('Helvetica', 8)
canvas.setFillColor(colors.HexColor('#666666'))
canvas.drawString(doc.leftMargin, 0.45*inch, f'{doc.title}')
canvas.drawRightString(letter[0]-doc.rightMargin, 0.45*inch, f'Page {doc.page}')
canvas.restoreState()
files = [
('hermes-agent-backlog-vision-report.md', 'Hermes Agent Research Report'),
('openclaw-backlog-vision-report.md', 'OpenClaw Research Report'),
]
for md_name, title in files:
md_path = base/md_name
pdf_path = base/md_name.replace('.md', '.pdf')
story = markdown_to_story(md_path)
doc = SimpleDocTemplate(str(pdf_path), pagesize=letter, leftMargin=0.7*inch, rightMargin=0.7*inch, topMargin=0.75*inch, bottomMargin=0.7*inch, title=title)
doc.build(story, onFirstPage=add_footer, onLaterPages=add_footer)
(base/'README.txt').write_text(
'NotebookLM packet\n\n'
'Generated files:\n'
'- hermes-agent-backlog-vision-report.pdf\n'
'- openclaw-backlog-vision-report.pdf\n\n'
'Source markdown:\n'
'- hermes-agent-backlog-vision-report.md\n'
'- openclaw-backlog-vision-report.md\n'
)
for p in sorted(base.iterdir()):
print(f'{p.name}\t{p.stat().st_size}')