Fix mypy type errors in reporting module
This commit is contained in:
460
docs/05_sprint_18_report_generation.md
Normal file
460
docs/05_sprint_18_report_generation.md
Normal file
@@ -0,0 +1,460 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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**
|
||||
Reference in New Issue
Block a user