Files
py-dvt-ate/docs/05_sprint_18_report_generation.md

14 KiB

Sprint 18: PDF Report Generation

Document ID DEV-002
Version 1.0.0
Status Draft
Author Kai Chappell
Created 2026-01-29
Last Updated 2026-01-29

Purpose

This document defines Sprint 18 of py-dvt-ate development: automated PDF report generation from test results. Reports are designed to be professional and well-presented for recruiters/clients evaluating the simulation platform.

For project context, see:

  • 01_requirements.md - What the system must do
  • 02_technical_specification.md - How to implement
  • 03_architecture_decisions.md - Why decisions were made
  • 04_development_plan.md - Phase 1 MVP sprints (1-17)

Feature Overview

Add automated PDF report generation with:

  • Professional, well-presented layout suitable for external stakeholders
  • Clean UX with easy download from CLI and dashboard
  • Test metadata, results table with pass/fail status, and measurement charts
  • Configurable company branding

Design Principles

Following existing project patterns:

  1. Small, Focused Commits - Each task = 1 commit, ~50-150 lines changed
  2. Stubs First - Define interfaces/types before implementation
  3. Test Alongside - Write tests immediately after implementation
  4. UK English - characterisation, behaviour, colour
  5. Minimal Context - Each task completable with knowledge of 1-3 files

Task Breakdown

Task 18.1: Add reporting dependencies to pyproject.toml

  • Add matplotlib>=3.8 to reports optional dependency group
  • Verify jinja2 and weasyprint already present
  • Files: pyproject.toml
  • Commit: "Add matplotlib to reports dependencies"

Task 18.2: Create report data models

  • Create src/py_dvt_ate/reporting/models.py
  • Define ReportConfig dataclass:
    • company_name: str - Company name for header
    • logo_path: Path | None - Optional logo image path
    • include_charts: bool - Whether to include charts
    • chart_dpi: int - Chart resolution
  • Define ReportData dataclass:
    • run: TestRun - Test run metadata
    • results: list[TestResult] - Scalar results with limits
    • measurements: pd.DataFrame | None - Time-series data
    • charts: dict[str, str] - Chart name to base64 PNG
  • Files: src/py_dvt_ate/reporting/models.py
  • Commit: "Add report data models"

Task 18.3: Create reporting exceptions

  • Create src/py_dvt_ate/reporting/exceptions.py
  • Define exception hierarchy:
    • ReportingError - Base exception
    • ReportGenerationError - General generation failure
    • TemplateRenderError - HTML rendering failure
    • PDFConversionError - HTML to PDF conversion failure
    • ChartGenerationError - Chart generation failure
  • Files: src/py_dvt_ate/reporting/exceptions.py
  • Commit: "Add reporting exception classes"

Task 18.4: Create CSS stylesheet for reports

  • Create src/py_dvt_ate/reporting/templates/styles.css
  • Professional styling:
    • A4 page setup with margins
    • Header with company branding
    • Footer with page numbers
    • Data tables with borders
    • Status badges (pass=green, fail=red, info=blue)
    • Summary cards with colour coding
    • Chart containers
    • Print-optimised with page breaks
  • Files: src/py_dvt_ate/reporting/templates/styles.css
  • Commit: "Add professional CSS stylesheet for reports"

Task 18.5: Create base HTML template

  • Create src/py_dvt_ate/reporting/templates/base.html
  • Jinja2 base template with:
    • <head> with CSS include
    • Header block with company name, logo, report metadata
    • Content block (for child templates)
    • Footer block with confidentiality notice, page numbers
  • WeasyPrint @page rules for PDF pagination
  • Files: src/py_dvt_ate/reporting/templates/base.html
  • Commit: "Add base HTML report template"

Task 18.6: Create test report template

  • Create src/py_dvt_ate/reporting/templates/test_report.html
  • Extends base.html with sections:
    • Test Overview: name, description, status, timestamps, duration, operator
    • Results Summary: total/pass/fail cards with counts
    • Results Table: parameter, value, unit, limits, pass/fail badge
    • Charts: voltage vs temperature (if available)
    • Configuration: test config JSON (optional)
  • Jinja2 filters for formatting (floats, dates)
  • Files: src/py_dvt_ate/reporting/templates/test_report.html
  • Commit: "Add test report HTML template"

Task 18.7: Implement HTML renderer

  • Create src/py_dvt_ate/reporting/renderers/__init__.py
  • Create src/py_dvt_ate/reporting/renderers/html.py
  • HTMLRenderer class:
    • Constructor takes ReportConfig
    • Uses jinja2.Environment with PackageLoader
    • render(report_data: ReportData) -> str method
    • Custom filters for number formatting
  • Template loading from py_dvt_ate.reporting.templates package
  • Files: src/py_dvt_ate/reporting/renderers/html.py, src/py_dvt_ate/reporting/renderers/__init__.py
  • Commit: "Implement HTML renderer with Jinja2"

Task 18.8: Implement PDF renderer

  • Create src/py_dvt_ate/reporting/renderers/pdf.py
  • PDFRenderer class:
    • render_to_file(html: str, output_path: Path) -> None
    • render_to_bytes(html: str) -> bytes
  • Use WeasyPrint HTML(string=html).write_pdf()
  • Handle WeasyPrint warnings gracefully
  • Files: src/py_dvt_ate/reporting/renderers/pdf.py
  • Commit: "Implement PDF renderer with WeasyPrint"

Task 18.9: Implement chart generator

  • Create src/py_dvt_ate/reporting/charts/__init__.py
  • Create src/py_dvt_ate/reporting/charts/matplotlib_charts.py
  • ChartGenerator class:
    • Constructor takes ReportConfig (for DPI)
    • _setup_style() - Configure matplotlib for professional appearance
    • generate_voltage_vs_temperature(measurements: DataFrame) -> str
      • Scatter plot with trend line
      • Calculate and display slope (ppm/C)
      • Return base64-encoded PNG
    • generate_all(run, results, measurements) -> dict[str, str]
      • Dispatch to appropriate chart methods based on test type
  • Use matplotlib.use('Agg') for non-interactive backend
  • Files: src/py_dvt_ate/reporting/charts/matplotlib_charts.py, src/py_dvt_ate/reporting/charts/__init__.py
  • Commit: "Implement matplotlib chart generator"

Task 18.10: Implement ReportGenerator class

  • Create src/py_dvt_ate/reporting/generator.py
  • IReportGenerator Protocol:
    • generate(run_id: UUID, output_path: Path | None) -> Path
    • generate_bytes(run_id: UUID) -> bytes
  • ReportGenerator class:
    • Constructor: repository, config, output_dir
    • Private: _html_renderer, _pdf_renderer, _chart_generator
    • _gather_data(run_id: UUID) -> ReportData
      • Fetch run, results, measurements from repository
      • Generate charts if measurements available
    • _generate_output_path(run: TestRun) -> Path
      • Format: {test_name}_{run_id_short}_{timestamp}.pdf
    • Error handling with appropriate exception types
  • Files: src/py_dvt_ate/reporting/generator.py
  • Commit: "Implement ReportGenerator class"

Task 18.11: Update reporting module exports

  • Update src/py_dvt_ate/reporting/__init__.py
  • Export public API:
    • ReportGenerator, IReportGenerator
    • ReportConfig, ReportData
    • All exception classes
  • Add module docstring with usage example
  • Lazy imports to handle missing optional dependencies
  • Files: src/py_dvt_ate/reporting/__init__.py
  • Commit: "Update reporting module public API"

Task 18.12: Add ReportingConfig to app config

  • Update src/py_dvt_ate/app/config.py
  • Add ReportingConfig Pydantic model:
    • company_name: str = "DVT Engineering"
    • logo_path: str | None = None
    • include_charts: bool = True
    • chart_dpi: int = 150
  • Add reporting: ReportingConfig to AppConfig
  • Files: src/py_dvt_ate/app/config.py
  • Commit: "Add ReportingConfig to application config"

Task 18.13: Add reporting section to default.yaml

  • Update config/default.yaml
  • Add reporting: section with all options
  • Document each option with comments
  • Files: config/default.yaml
  • Commit: "Add reporting configuration to default.yaml"

Task 18.14: Add list-runs CLI command

  • Update src/py_dvt_ate/app/cli.py
  • Add list-runs command:
    • --limit option (default 10)
    • --config option for config file
  • Output format: {id:8} {test_name:15} {status:8} {timestamp}
  • Load repository from config
  • Files: src/py_dvt_ate/app/cli.py
  • Commit: "Add list-runs CLI command"

Task 18.15: Add export-report CLI command

  • Update src/py_dvt_ate/app/cli.py
  • Add export-report command:
    • run_id argument (required)
    • --output / -o option for output path
    • --company option for company name override
    • --config option for config file
  • Support short (8-char) and full UUID lookup
  • Display progress and result path
  • Files: src/py_dvt_ate/app/cli.py
  • Commit: "Add export-report CLI command"

Task 18.16: Add PDF download to dashboard

  • Update src/py_dvt_ate/app/dashboard/app.py
  • In results viewer page, add:
    • "Generate PDF Report" button (primary)
    • st.download_button for PDF download
    • Progress spinner during generation
    • Error handling for missing dependencies
  • Store generated PDF in st.session_state
  • Files: src/py_dvt_ate/app/dashboard/app.py
  • Commit: "Add PDF download button to dashboard"

Task 18.17: Add reporting unit tests

  • Create tests/unit/reporting/__init__.py
  • Create tests/unit/reporting/test_models.py
    • Test ReportConfig and ReportData creation
    • Test default values
  • Create tests/unit/reporting/test_html_renderer.py
    • Test template rendering with mock data
    • Test custom filters
  • Create tests/unit/reporting/test_chart_generator.py
    • Test chart generation produces valid base64
    • Test with sample DataFrame
  • Files: tests/unit/reporting/
  • Commit: "Add reporting unit tests"

Task 18.18: Add reporting integration test

  • Create tests/integration/test_report_generation.py
  • End-to-end test:
    • Create test run with sample results in repository
    • Generate PDF report
    • Verify PDF file created and non-empty
    • Optionally verify PDF structure (page count)
  • Use pytest fixtures for repository setup
  • Files: tests/integration/test_report_generation.py
  • Commit: "Add report generation integration test"

Task 18.19: Update CHANGELOG

  • Update CHANGELOG.md
  • Add ## [Unreleased] section if not present
  • Document:
    • New export-report CLI command
    • New list-runs CLI command
    • Dashboard PDF download feature
    • Reporting module with PDF/HTML generation
  • Files: CHANGELOG.md
  • Commit: "Update CHANGELOG with report generation feature"

File Structure (New Files)

src/py_dvt_ate/reporting/
├── __init__.py              # Task 18.11 - Public API
├── models.py                # Task 18.2 - ReportConfig, ReportData
├── exceptions.py            # Task 18.3 - Exception hierarchy
├── generator.py             # Task 18.10 - ReportGenerator
├── renderers/
│   ├── __init__.py          # Task 18.7
│   ├── html.py              # Task 18.7 - HTMLRenderer
│   └── pdf.py               # Task 18.8 - PDFRenderer
├── charts/
│   ├── __init__.py          # Task 18.9
│   └── matplotlib_charts.py # Task 18.9 - ChartGenerator
└── templates/
    ├── styles.css           # Task 18.4 - CSS
    ├── base.html            # Task 18.5 - Base template
    └── test_report.html     # Task 18.6 - Report template

tests/unit/reporting/
├── __init__.py              # Task 18.17
├── test_models.py           # Task 18.17
├── test_html_renderer.py    # Task 18.17
└── test_chart_generator.py  # Task 18.17

tests/integration/
└── test_report_generation.py # Task 18.18

Files to Modify

File Tasks Changes
pyproject.toml 18.1 Add matplotlib to reports deps
src/py_dvt_ate/app/config.py 18.12 Add ReportingConfig
config/default.yaml 18.13 Add reporting section
src/py_dvt_ate/app/cli.py 18.14, 18.15 Add list-runs, export-report
src/py_dvt_ate/app/dashboard/app.py 18.16 Add PDF download
CHANGELOG.md 18.19 Document new features

Dependencies

Package Version Purpose
jinja2 >=3.1 HTML template rendering
weasyprint >=60.0 HTML to PDF conversion
matplotlib >=3.8 Chart generation

All in [project.optional-dependencies] reports group.

Install with: pip install py-dvt-ate[reports]


Verification

CLI Verification

# List recent test runs
py-dvt-ate list-runs

# Generate PDF report
py-dvt-ate export-report <run_id>

# With options
py-dvt-ate export-report <run_id> -o ./my_report.pdf --company "Acme Corp"

# View generated PDF
xdg-open ./data/reports/*.pdf

Dashboard Verification

# Start dashboard
streamlit run src/py_dvt_ate/app/dashboard/app.py

# In browser:
# 1. Navigate to Results Viewer
# 2. Select a test run
# 3. Click "Generate PDF Report"
# 4. Click "Download PDF Report"
# 5. Open downloaded PDF

Test Verification

# Run unit tests
pytest tests/unit/reporting/ -v

# Run integration test
pytest tests/integration/test_report_generation.py -v

# Check coverage
pytest tests/unit/reporting/ --cov=py_dvt_ate.reporting --cov-report=term-missing

Task Progress

Task Status Description
18.1 pending Add matplotlib dependency
18.2 pending Report data models
18.3 pending Reporting exceptions
18.4 pending CSS stylesheet
18.5 pending Base HTML template
18.6 pending Test report template
18.7 pending HTML renderer
18.8 pending PDF renderer
18.9 pending Chart generator
18.10 pending ReportGenerator class
18.11 pending Module exports
18.12 pending App config update
18.13 pending default.yaml update
18.14 pending list-runs CLI
18.15 pending export-report CLI
18.16 pending Dashboard download
18.17 pending Unit tests
18.18 pending Integration test
18.19 pending CHANGELOG update

End of Sprint 18 Plan