Claude
Skills
Sign in
Back

pdf-report-generator

Included with Lifetime
$97 forever

Automatically generate PDF reports from construction data. Create formatted project reports with charts and tables.

Writing & Docs

What this skill does

# PDF Report Generator

## Business Case

### Problem Statement
Report generation challenges:
- Manual report creation is time-consuming
- Inconsistent formatting
- Data aggregation from multiple sources
- Repetitive weekly/monthly reports

### Solution
Automated PDF report generation from project data with templates, charts, and customizable sections.

## Technical Implementation

```python
import pandas as pd
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from datetime import date, datetime
from enum import Enum
from io import BytesIO


class ReportType(Enum):
    PROGRESS = "progress"
    COST = "cost"
    SAFETY = "safety"
    QUALITY = "quality"
    EXECUTIVE = "executive"
    WEEKLY = "weekly"
    MONTHLY = "monthly"


class SectionType(Enum):
    HEADER = "header"
    TEXT = "text"
    TABLE = "table"
    CHART = "chart"
    KPI_CARDS = "kpi_cards"
    IMAGE = "image"
    PAGE_BREAK = "page_break"


@dataclass
class ReportSection:
    section_type: SectionType
    title: str = ""
    content: Any = None
    style: Dict[str, Any] = field(default_factory=dict)


@dataclass
class KPICard:
    name: str
    value: Any
    unit: str = ""
    target: Any = None
    status: str = "normal"  # normal, warning, critical, good


@dataclass
class ChartConfig:
    chart_type: str  # bar, line, pie
    data: Dict[str, List]
    title: str = ""
    x_label: str = ""
    y_label: str = ""


class PDFReportGenerator:
    """Generate PDF reports from construction project data."""

    def __init__(self, project_name: str, report_type: ReportType):
        self.project_name = project_name
        self.report_type = report_type
        self.sections: List[ReportSection] = []
        self.metadata: Dict[str, Any] = {
            'author': '',
            'date': date.today(),
            'version': '1.0'
        }

    def set_metadata(self, author: str = "", report_date: date = None, version: str = "1.0"):
        """Set report metadata."""
        self.metadata['author'] = author
        self.metadata['date'] = report_date or date.today()
        self.metadata['version'] = version

    def add_header(self, title: str, subtitle: str = ""):
        """Add report header section."""
        self.sections.append(ReportSection(
            section_type=SectionType.HEADER,
            title=title,
            content={'subtitle': subtitle, 'date': self.metadata['date'].isoformat()}
        ))

    def add_text(self, title: str, content: str):
        """Add text section."""
        self.sections.append(ReportSection(
            section_type=SectionType.TEXT,
            title=title,
            content=content
        ))

    def add_table(self, title: str, df: pd.DataFrame, style: Dict[str, Any] = None):
        """Add table section from DataFrame."""
        self.sections.append(ReportSection(
            section_type=SectionType.TABLE,
            title=title,
            content=df.to_dict('records'),
            style=style or {}
        ))

    def add_kpi_cards(self, title: str, kpis: List[KPICard]):
        """Add KPI cards section."""
        self.sections.append(ReportSection(
            section_type=SectionType.KPI_CARDS,
            title=title,
            content=[{
                'name': k.name,
                'value': k.value,
                'unit': k.unit,
                'target': k.target,
                'status': k.status
            } for k in kpis]
        ))

    def add_chart(self, title: str, chart_config: ChartConfig):
        """Add chart section."""
        self.sections.append(ReportSection(
            section_type=SectionType.CHART,
            title=title,
            content={
                'type': chart_config.chart_type,
                'data': chart_config.data,
                'x_label': chart_config.x_label,
                'y_label': chart_config.y_label
            }
        ))

    def add_page_break(self):
        """Add page break."""
        self.sections.append(ReportSection(section_type=SectionType.PAGE_BREAK))

    def generate_progress_report(self, data: Dict[str, Any]):
        """Generate standard progress report."""

        self.add_header(
            f"{self.project_name} - Progress Report",
            f"Report Date: {self.metadata['date']}"
        )

        # KPIs
        kpis = [
            KPICard("Overall Progress", f"{data.get('overall_progress', 0)}%", target="100%",
                   status="good" if data.get('overall_progress', 0) >= data.get('planned_progress', 0) else "warning"),
            KPICard("SPI", f"{data.get('spi', 1.0):.2f}", target="1.00",
                   status="good" if data.get('spi', 1) >= 0.95 else "critical"),
            KPICard("CPI", f"{data.get('cpi', 1.0):.2f}", target="1.00",
                   status="good" if data.get('cpi', 1) >= 0.95 else "critical"),
            KPICard("Days Remaining", str(data.get('days_remaining', 0)), "days")
        ]
        self.add_kpi_cards("Key Performance Indicators", kpis)

        # Progress summary
        self.add_text("Executive Summary", data.get('summary', 'No summary provided.'))

        # Activities table
        if 'activities' in data:
            activities_df = pd.DataFrame(data['activities'])
            self.add_table("Activity Status", activities_df)

        # Progress chart
        if 'progress_history' in data:
            self.add_chart("Progress Trend", ChartConfig(
                chart_type="line",
                data=data['progress_history'],
                title="Progress Over Time",
                x_label="Date",
                y_label="Progress %"
            ))

        # Issues
        if 'issues' in data:
            self.add_text("Current Issues", "\n".join(f"- {issue}" for issue in data['issues']))

    def generate_cost_report(self, data: Dict[str, Any]):
        """Generate cost report."""

        self.add_header(
            f"{self.project_name} - Cost Report",
            f"Period: {data.get('period', 'Current')}"
        )

        # Cost KPIs
        budget = data.get('budget', 0)
        actual = data.get('actual_cost', 0)
        variance = budget - actual

        kpis = [
            KPICard("Budget", f"${budget:,.0f}"),
            KPICard("Actual Cost", f"${actual:,.0f}"),
            KPICard("Variance", f"${variance:,.0f}",
                   status="good" if variance >= 0 else "critical"),
            KPICard("CPI", f"{data.get('cpi', 1.0):.2f}",
                   status="good" if data.get('cpi', 1) >= 0.95 else "warning")
        ]
        self.add_kpi_cards("Cost Summary", kpis)

        # Cost breakdown
        if 'cost_breakdown' in data:
            breakdown_df = pd.DataFrame(data['cost_breakdown'])
            self.add_table("Cost Breakdown by Category", breakdown_df)

        # Cost trend
        if 'cost_history' in data:
            self.add_chart("Cost Trend", ChartConfig(
                chart_type="bar",
                data=data['cost_history'],
                title="Monthly Cost",
                x_label="Month",
                y_label="Cost ($)"
            ))

    def generate_safety_report(self, data: Dict[str, Any]):
        """Generate safety report."""

        self.add_header(
            f"{self.project_name} - Safety Report",
            f"Period: {data.get('period', 'Current')}"
        )

        # Safety KPIs
        kpis = [
            KPICard("Days Without Incident", str(data.get('days_without_incident', 0)), "days"),
            KPICard("TRIR", f"{data.get('trir', 0):.2f}",
                   status="good" if data.get('trir', 0) <= 2 else "critical"),
            KPICard("Near Misses", str(data.get('near_misses', 0))),
            KPICard("Safety Observations", str(data.get('observations', 0)))
        ]
        self.add_kpi_cards("Safety Metrics", kpis)

        # Incidents
        if 'incidents' in data and data['incidents']:
            incidents_df = pd.DataFrame(data['incidents'])
            self.add_t

Related in Writing & Docs