| name | python-document-generation |
| description | Use when Python must generate downloadable Excel, Word, or PDF artefacts for application users with branding, charts, tables, formulas, and delivery controls; use the format-specific skill when only one document type needs manual production. |
| metadata | {"portable":true,"compatible_with":["claude-code","codex"]} |
Python Document Generation
Acknowledgement: Shared by Peter Bamuhigire, techguypeter.com, +256 784 464178.
Use When
- Use when generating downloadable Excel dashboards, Word documents, or PDF reports from Python for end users of web, Android, and iOS SaaS apps — designer-grade branded output with charts, tables, formulas, conditional formatting, and multi-page layouts.
- The task needs reusable judgment, domain constraints, or a proven workflow rather than ad hoc advice.
Do Not Use When
- The task is unrelated to
python-document-generation or would be better handled by a more specific companion skill.
- The request only needs a trivial answer and none of this skill's constraints or references materially help.
Python Document Generation Required Context
- Gather relevant project context, constraints, and the concrete problem to solve; load
references only as needed.
- Confirm the desired deliverable: design, code, review, migration plan, audit, or documentation.
Python Document Generation Core Method Notes
- Read this
SKILL.md first, then load only the referenced deep-dive files that are necessary for the task.
- Apply the ordered guidance, checklists, and decision rules in this skill instead of cherry-picking isolated snippets.
- Produce the deliverable with assumptions, risks, and follow-up work made explicit when they matter.
Quality Standards
- Keep outputs execution-oriented, concise, and aligned with the repository's baseline engineering standards.
- Preserve compatibility with existing project conventions unless the skill explicitly requires a stronger standard.
- Prefer deterministic, reviewable steps over vague advice or tool-specific magic.
Python Document Generation Existing Failure Notes
- Treating examples as copy-paste truth without checking fit, constraints, or failure modes.
- Loading every reference file by default instead of using progressive disclosure.
Python Document Generation Core Deliverables
- A concrete result that fits the task: implementation guidance, review findings, architecture decisions, templates, or generated artifacts.
- Clear assumptions, tradeoffs, or unresolved gaps when the task cannot be completed from available context alone.
- References used, companion skills, or follow-up actions when they materially improve execution.
Evidence Produced
| Category | Artifact | Format | Example |
|---|
| Correctness | Document export test plan | Markdown doc covering Excel/Word/PDF rendering for layout, fonts, images, and pagination | docs/python/doc-gen-tests.md |
Python Document Generation Source Notes
- Use the
references/ directory for deep detail after reading the core workflow below.
Inputs
| Artefact | Source or provider | Requirement | If absent |
|---|
| Verified data, output schema, brand assets, and delivery constraints | application and content owner | required | Return a generator contract if execution or rendering is unavailable |
Capability contract
Read access to source data, generator code, brand assets, and delivery constraints is required. Code edits and execution need explicit authority; deployment, storage writes, and user delivery require separate production permission.
Degraded mode
If dependencies, execution, or rendering are unavailable, return a qualified generator contract or patch, identify untested formats, and leave reconciliation and visual checks unassessed.
Decision rules
| Choice | Action | Failure avoided |
|---|
| Large or slow document may exceed request limits | Queue generation and expose status | Request timeout or duplicate generation |
Outputs
| Artefact | Consumer | Observable acceptance condition |
|---|
| Generator, generated artefact, and validation evidence | application user and engineering team | File opens, data reconciles, and format-specific render checks pass |
Python Document Generation Evidence Notes 1
- Preserve fixture identifiers, library choice, source reconciliation, performance observations, generated-file checks, and render results.
Worked example
Generate a branded report from fixed test data, verify totals against the source, render representative pages, and test the download response.
How we produce beautiful, branded, downloadable Excel / Word / PDF files from Python. These are end-user artifacts — professional enough to send to clients, file with auditors, or hand to executives.
Prerequisites: Load python-modern-standards and python-saas-integration before this skill. Load python-data-analytics when the input is a DataFrame.
When this skill applies
- Generating multi-sheet Excel dashboards, financial statements, audit exports.
- Producing branded Word documents: reports, proposals, certificates, letters.
- Producing PDF reports: executive summaries, invoices, statements, audit trails.
- Embedding charts and tables in downloadable files.
- Delivering files to web, Android, and iOS clients with signed URLs.
Output strategy — sync vs async
File < 500KB AND < 1s to generate -> Sidecar, return file bytes or stream
File 500KB – 5MB -> Sidecar, write to storage, return signed URL
File > 5MB OR > 2s to generate -> Worker, enqueue job, notify on completion
Scheduled / recurring reports -> Worker
Sync path: FastAPI endpoint returns FileResponse or a signed URL to a just-written file.
Async path: PHP enqueues job → worker generates file → worker writes to storage → worker notifies PHP via webhook or DB flag → PHP emails user or marks as ready.
See python-saas-integration → references/file-handoff.md for the full delivery pattern.
Excel — openpyxl vs xlsxwriter
Both libraries are good. Pick per use case:
| Feature | openpyxl | xlsxwriter |
|---|
| Read existing .xlsx | Yes | No |
| Modify existing .xlsx | Yes | No |
| Write new .xlsx | Yes | Yes |
| Charts | Basic | Rich, Excel-like |
| Conditional formatting | Yes | Yes (more features) |
| Pivot tables | Read only | No |
| Write speed (large files) | Slower | Faster (memory-optimized mode) |
| Formulas | Yes | Yes |
| Images | Yes | Yes |
Rule: xlsxwriter for new multi-sheet dashboards with heavy formatting. openpyxl when you need to read/modify a template the client provided or when you need pivot tables.
Excel dashboard patterns
A well-designed dashboard workbook has a consistent structure:
- Cover sheet — tenant logo, report title, reporting period, generation timestamp.
- Summary KPIs — the 5–10 numbers that matter, with conditional formatting (red/amber/green).
- Detail sheets — one per entity (customers, products, regions) with sortable/filterable tables.
- Chart sheets — trends over time, comparisons, breakdowns.
- Raw data sheet — hidden or last; contains the underlying data so the user can audit.
Skeleton (xlsxwriter):
import xlsxwriter
wb = xlsxwriter.Workbook(path, {"constant_memory": True, "default_date_format": "yyyy-mm-dd"})
brand = Brand.from_tenant(tenant)
fmt_title = wb.add_format({"bold": True, "font_size": 18, "font_color": brand.primary, "font_name": "Inter"})
fmt_kpi_label = wb.add_format({"bold": True, "font_color": "#555", "font_size": 10})
fmt_kpi_value = wb.add_format({"bold": True, "font_size": 24, "font_color": brand.primary, "num_format": "#,##0"})
fmt_header = wb.add_format({"bold": True, "bg_color": brand.primary, "font_color": "white", "border": 1})
fmt_currency = wb.add_format({"num_format": "#,##0.00"})
fmt_percent = wb.add_format({"num_format": "0.0%"})
cover = wb.add_worksheet("Cover")
cover.hide_gridlines(2)
cover.insert_image("B2", logo_path, {"x_scale": 0.5, "y_scale": 0.5})
cover.write("B8", f"{tenant.name} — Sales Dashboard", fmt_title)
cover.write("B9", , fmt_kpi_label)
cover.write(, , fmt_kpi_label)
cover.set_column(, )
Full multi-sheet dashboard template (KPIs, charts, conditional formatting, formulas, pivots when openpyxl, protection) in references/excel-dashboard-patterns.md.
Word — python-docx with branded letterhead
python-docx is the standard. It gives fine control over paragraphs, runs, tables, and sections, but some things (page borders, complex headers) are set via direct OXML.
Structure of a branded report:
- Header with logo + tenant name on every page.
- Footer with page numbers + report metadata.
- Title page (first page different) — title, subtitle, date.
- Body sections — headings styled consistently (heading levels mapped to your brand type scale).
- Tables — branded header row, zebra striping via row shading.
- Embedded images/charts — rendered via matplotlib PNG export.
See references/word-python-docx.md for the letterhead template, style setup, and the helpers we use to avoid fighting with OXML for common needs.
PDF — reportlab or weasyprint
Two philosophies:
- reportlab (platypus): programmatic canvas / flowables. Full control, pixel-perfect, best for financial statements, audit trails, anything tabular where layout precision matters.
- weasyprint: render HTML + CSS to PDF. Best when your report looks like a web page, when designers give you HTML, or when you want print styles that mirror your web dashboard.
Pick weasyprint when: marketing/brand-heavy layouts, designer-provided HTML, reusing existing CSS. Requires Cairo/Pango system libs on the server.
Pick reportlab when: data-dense financial output, fine-grained paginated tables, no system dependencies.
Minimal reportlab example:
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image, PageBreak
)
doc = SimpleDocTemplate(path, pagesize=A4,
leftMargin=20*mm, rightMargin=20*mm,
topMargin=25*mm, bottomMargin=20*mm)
styles = getSampleStyleSheet()
h1 = ParagraphStyle("h1", parent=styles["Heading1"], textColor=colors.HexColor(brand.primary), fontName="Helvetica-Bold")
story = [
Image(logo_path, width=40*mm, height=15*mm),
Spacer(1, 10*mm),
Paragraph(f"{tenant.name} — Monthly Report", h1),
Paragraph(f"Period: {period}", styles["Normal"]),
Spacer(1, 8*mm),
Table(table_data, style=TableStyle([...]), repeatRows=1),
]
doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
See references/pdf-reportlab.md and references/pdf-weasyprint.md.
Charts for embedding
Static PNG/SVG from matplotlib (for clarity, use a limited palette aligned to brand; set figure DPI to 150+ for print quality).
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(7, 3.5), dpi=150)
ax.plot(df.index, df["mrr"], color=brand.primary, linewidth=2)
ax.fill_between(df.index, df["mrr"], color=brand.primary, alpha=0.1)
ax.set_title("MRR trend", pad=12)
ax.spines[["top", "right"]].set_visible(False)
fig.tight_layout()
fig.savefig(png_path, dpi=150, bbox_inches="tight")
plt.close(fig)
Never use pyplot stateful API in a worker. Always create explicit fig, ax. Always plt.close(fig) — matplotlib leaks memory otherwise and workers will OOM after a few hundred charts.
See references/charts-for-embedding.md for plotly static export and the style guide for chart brand consistency.
Branding system
One source of truth for brand across all output types.
@dataclass(frozen=True)
class Brand:
primary: str
secondary: str
text: str = "#111827"
muted: str = "#6B7280"
success: str = "#059669"
danger: str = "#DC2626"
warning: str = "#D97706"
font_family: str = "Inter"
logo_path: Path
footer_tagline: str = ""
@classmethod
def from_tenant(cls, tenant: Tenant) -> "Brand":
return cls(
primary=tenant.brand_color_primary or "#0B5FFF",
secondary=tenant.brand_color_secondary or "#6B7280",
logo_path=tenant.logo_path,
footer_tagline=tenant.footer_tagline or "",
)
Reuse the same Brand object in Excel (format colors), Word (style colors, logo), and PDF (reportlab colors, weasyprint CSS vars). See references/branding-system.md.
Delivery to web / Android / iOS
- MIME types:
- xlsx:
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
- docx:
application/vnd.openxmlformats-officedocument.wordprocessingml.document
- pdf:
application/pdf
- Filenames: use
Content-Disposition: attachment; filename*=UTF-8''<url-encoded>.xlsx. Support non-ASCII tenant names.
- Signed URLs: short-TTL (5–30 min), single-use where possible. Authorization is the URL itself.
- Mobile handoff: Android
DownloadManager / iOS URLSession both handle signed URLs fine. For in-app preview, return a PDF (both platforms have native previewers); Excel/Word require a system app.
- Filename convention:
<tenant-slug>_<report-type>_<period>_<timestamp>.<ext> — always include period and timestamp so users can keep multiple versions.
See references/delivery-and-downloads.md.
Async generation flow (worker)
1. PHP user requests report (clicks Generate)
2. PHP enqueues job with payload + idempotency_key
-> responds 202 Accepted with job_id
3. Client polls /jobs/{id} every 3s OR PHP sends push/email when ready
4. Worker picks up job:
a. Loads data (pandas / SQL)
b. Generates file (xlsxwriter / python-docx / reportlab)
c. Writes to storage under tenant_id/reports/{job_id}/...
d. Updates job status: completed, file_url=signed_url
5. Client downloads the file
Always write the file to a temp path first, then atomic-rename on success. Never expose partial files.
Performance
- Stream when possible: xlsxwriter
constant_memory=True for > 100K rows. openpyxl has write_only=True mode.
- Pre-compute totals in pandas, write numbers (not formulas) for huge workbooks — formulas kill open time in Excel.
- Charts are expensive: generate in parallel only if
matplotlib.use("Agg") + a process pool. Threads won't help for matplotlib.
- Memory cap: worker processes should have RLIMIT_AS (Linux) or systemd
MemoryMax= set. Kill runaway jobs rather than swap.
- Cache brand assets: load logo bytes once per worker process, not per job.
See references/performance.md.
Python Document Generation Additional Failure Modes 2
- Generating a 10,000-row Excel inside a web request — use the worker.
- Embedding raw bytes of a huge image per row — link to external resource or resize to thumbnail.
- Not closing matplotlib figures — OOM after many jobs.
- Using pyplot global state in async/threaded code — race conditions, garbled charts.
- Building PDF pages by hand with
canvas.Canvas when platypus flowables would do — fragile.
- Mixing date types in one column — Excel will auto-interpret badly. Keep dtypes consistent.
- Storing generated files indefinitely — enforce TTL + cleanup.
- Using tenant-supplied filename unsanitized — directory traversal risk.
References
references/excel-openpyxl-vs-xlsxwriter.md
references/excel-dashboard-patterns.md
references/word-python-docx.md
references/pdf-reportlab.md
references/pdf-weasyprint.md
references/charts-for-embedding.md
references/branding-system.md
references/delivery-and-downloads.md
references/performance.md
See also
excel-spreadsheets skill — design principles for professional Excel output (complements the Python implementation here).
professional-word-output skill — design principles for Word documents.
data-visualization skill — Knaflic's storytelling principles; apply these to every chart.
report-print-pdf skill — HTML/mPDF counterpart in PHP.
Workflow
- Confirm output formats, verified data, brand assets, delivery limits, and test fixtures.
- Choose the format library and synchronous or queued delivery path.
- Stop when data totals, required fonts, or output schema cannot be verified.
- Generate, reconcile, open, and render the artefact; recover by isolating the failing format or returning a qualified generator contract.
Python Document Generation Evidence Notes 2
| Evidence | Consumer | Acceptance |
|---|
| Generator tests, reconciliation, and render-QA record | Engineering and release owner | Files open, source totals match, and format checks are recorded |
Anti-Patterns
- Choosing a library before checking format needs. Fix: decide from required features.
- Trusting generated totals without reconciliation. Fix: compare against source fixtures.
- Blocking a request with a large document. Fix: queue it and expose status.
- Shipping a file that was never opened or rendered. Fix: inspect representative outputs.
- Hiding an unavailable font or renderer. Fix: mark the visual check not assessed.
Reference Index