interactive-report-generator

Interactive Report Generator

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "interactive-report-generator" with this command: npx skills add vamseeachanta/workspace-hub/vamseeachanta-workspace-hub-interactive-report-generator

Interactive Report Generator

Create professional, interactive HTML reports with Plotly visualizations.

Quick Start

Generate report from data

/interactive-report-generator --data results.csv --output report.html

Create dashboard

/interactive-report-generator --type dashboard --config dashboard.yaml

Generate from analysis results

/interactive-report-generator --source analysis_output/

When to Use

USE when:

  • Creating analysis reports

  • Generating data visualizations

  • Building dashboards

  • Presenting results to stakeholders

DON'T USE when:

  • Static images are required

  • PDF export is primary format

  • Real-time streaming data

Prerequisites

  • Python 3.9+

  • plotly>=5.15.0

  • pandas>=2.0.0

  • Data in CSV/DataFrame format

Overview

Generates interactive HTML reports compliant with workspace-hub HTML_REPORTING_STANDARDS.md:

  • Interactive plots only - No static matplotlib

  • CSV data import - Relative paths

  • Responsive design - Works on all devices

  • Professional styling - Consistent theming

  • Export options - PNG, SVG from plots

Core Templates

  1. Basic Report Template

""" ABOUTME: Interactive HTML report generator ABOUTME: Creates Plotly-based reports from analysis data """

import pandas as pd import plotly.express as px import plotly.graph_objects as go from plotly.subplots import make_subplots from pathlib import Path from datetime import datetime from typing import Dict, List, Any, Optional

def generate_report( data: pd.DataFrame, output_path: Path, title: str = "Analysis Report", config: Optional[Dict[str, Any]] = None ) -> Path: """ Generate interactive HTML report from data.

Args:
    data: DataFrame with analysis results
    output_path: Path to save HTML report
    title: Report title
    config: Optional configuration dictionary

Returns:
    Path to generated report
"""
config = config or {}

# Create HTML structure
html_content = f"""

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{title}</title> <script src="https://cdn.plot.ly/plotly-latest.min.js">&#x3C;/script> <style> * {{ margin: 0; padding: 0; box-sizing: border-box; }} body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; background-color: #f5f5f5; color: #333; line-height: 1.6; }} .report-header {{ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px; text-align: center; }} .report-header h1 {{ font-size: 2.5em; margin-bottom: 10px; }} .report-header .metadata {{ opacity: 0.9; font-size: 0.9em; }} .container {{ max-width: 1400px; margin: 0 auto; padding: 20px; }} .summary-cards {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 30px 0; }} .card {{ background: white; border-radius: 12px; padding: 25px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); text-align: center; }} .card .label {{ font-size: 0.85em; color: #666; text-transform: uppercase; letter-spacing: 1px; }} .card .value {{ font-size: 2.5em; font-weight: bold; color: #667eea; margin: 10px 0; }} .plot-container {{ background: white; border-radius: 12px; padding: 25px; margin: 20px 0; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); }} .plot-container h2 {{ margin-bottom: 20px; color: #333; border-bottom: 2px solid #667eea; padding-bottom: 10px; }} .footer {{ text-align: center; padding: 30px; color: #666; font-size: 0.85em; }} @media (max-width: 768px) {{ .report-header h1 {{ font-size: 1.8em; }} .card .value {{ font-size: 2em; }} }} </style> </head> <body> <div class="report-header"> <h1>{title}</h1> <div class="metadata"> Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | Records: {len(data):,} </div> </div>

&#x3C;div class="container">
    &#x3C;!-- Summary Cards -->
    &#x3C;div class="summary-cards" id="summary-cards">
        &#x3C;!-- Generated by JavaScript -->
    &#x3C;/div>

    &#x3C;!-- Plots -->
    &#x3C;div id="plots-container">
        &#x3C;!-- Generated dynamically -->
    &#x3C;/div>
&#x3C;/div>

&#x3C;div class="footer">
    Generated with Interactive Report Generator | workspace-hub
&#x3C;/div>

</body> </html> """

# Write HTML
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(html_content)

return output_path

def create_time_series_plot( data: pd.DataFrame, x_col: str, y_cols: List[str], title: str = "Time Series" ) -> go.Figure: """ Create interactive time series plot.

Args:
    data: DataFrame with time series data
    x_col: Column for x-axis (typically datetime)
    y_cols: Columns for y-axis values
    title: Plot title

Returns:
    Plotly Figure object
"""
fig = go.Figure()

for col in y_cols:
    fig.add_trace(go.Scatter(
        x=data[x_col],
        y=data[col],
        mode='lines+markers',
        name=col,
        hovertemplate='%{x}&#x3C;br>%{y:.2f}&#x3C;extra>&#x3C;/extra>'
    ))

fig.update_layout(
    title=title,
    xaxis_title=x_col,
    yaxis_title="Value",
    hovermode='x unified',
    template='plotly_white',
    height=500
)

return fig

def create_distribution_plot( data: pd.DataFrame, column: str, title: str = "Distribution" ) -> go.Figure: """ Create interactive histogram/distribution plot.

Args:
    data: DataFrame with data
    column: Column to plot distribution
    title: Plot title

Returns:
    Plotly Figure object
"""
fig = px.histogram(
    data,
    x=column,
    title=title,
    template='plotly_white',
    nbins=50
)

fig.update_layout(
    xaxis_title=column,
    yaxis_title="Count",
    height=400
)

return fig

def create_correlation_heatmap( data: pd.DataFrame, columns: Optional[List[str]] = None, title: str = "Correlation Matrix" ) -> go.Figure: """ Create interactive correlation heatmap.

Args:
    data: DataFrame with numeric data
    columns: Optional list of columns to include
    title: Plot title

Returns:
    Plotly Figure object
"""
if columns:
    corr_data = data[columns].corr()
else:
    corr_data = data.select_dtypes(include='number').corr()

fig = px.imshow(
    corr_data,
    title=title,
    template='plotly_white',
    color_continuous_scale='RdBu_r',
    zmin=-1,
    zmax=1
)

fig.update_layout(height=500)

return fig

def create_dashboard( data: pd.DataFrame, output_path: Path, config: Dict[str, Any] ) -> Path: """ Create multi-panel dashboard.

Args:
    data: DataFrame with analysis data
    output_path: Path to save dashboard HTML
    config: Dashboard configuration

Returns:
    Path to generated dashboard
"""
# Create subplots
fig = make_subplots(
    rows=2, cols=2,
    subplot_titles=(
        "Time Series",
        "Distribution",
        "Scatter Plot",
        "Summary Statistics"
    ),
    specs=[
        [{"type": "scatter"}, {"type": "histogram"}],
        [{"type": "scatter"}, {"type": "table"}]
    ]
)

# Add plots based on config
numeric_cols = data.select_dtypes(include='number').columns.tolist()

if len(numeric_cols) >= 2:
    # Time series or line plot
    fig.add_trace(
        go.Scatter(
            x=data.index,
            y=data[numeric_cols[0]],
            mode='lines',
            name=numeric_cols[0]
        ),
        row=1, col=1
    )

    # Distribution
    fig.add_trace(
        go.Histogram(x=data[numeric_cols[0]], name="Distribution"),
        row=1, col=2
    )

    # Scatter
    fig.add_trace(
        go.Scatter(
            x=data[numeric_cols[0]],
            y=data[numeric_cols[1]],
            mode='markers',
            name="Correlation"
        ),
        row=2, col=1
    )

# Summary statistics table
stats = data.describe().reset_index()
fig.add_trace(
    go.Table(
        header=dict(values=list(stats.columns)),
        cells=dict(values=[stats[col].round(2) for col in stats.columns])
    ),
    row=2, col=2
)

fig.update_layout(
    height=800,
    showlegend=True,
    title_text="Analysis Dashboard"
)

# Save
fig.write_html(output_path, include_plotlyjs='cdn')

return output_path

2. Report Generator Class

""" ABOUTME: Full-featured report generator class ABOUTME: Supports multiple plot types and configurations """

import pandas as pd import plotly.graph_objects as go from plotly.subplots import make_subplots from pathlib import Path from typing import Dict, List, Any, Optional from datetime import datetime import json

class InteractiveReportGenerator: """Generate interactive HTML reports with Plotly."""

def __init__(self, config: Optional[Dict[str, Any]] = None):
    """
    Initialize report generator.

    Args:
        config: Optional configuration dictionary
    """
    self.config = config or {}
    self.plots: List[go.Figure] = []
    self.summary_stats: Dict[str, Any] = {}

    # Default styling
    self.theme = self.config.get('theme', {
        'primary_color': '#667eea',
        'secondary_color': '#764ba2',
        'background': '#f5f5f5',
        'card_bg': '#ffffff',
        'text_color': '#333333'
    })

def add_data(self, data: pd.DataFrame, name: str = "data"):
    """Add data source to report."""
    self.data = data
    self.data_name = name
    self._calculate_summary()

def _calculate_summary(self):
    """Calculate summary statistics."""
    if hasattr(self, 'data'):
        self.summary_stats = {
            'total_records': len(self.data),
            'columns': len(self.data.columns),
            'numeric_cols': len(self.data.select_dtypes(include='number').columns),
            'missing_values': self.data.isnull().sum().sum()
        }

def add_time_series(
    self,
    x_col: str,
    y_cols: List[str],
    title: str = "Time Series Analysis"
):
    """Add time series plot."""
    fig = go.Figure()

    for col in y_cols:
        fig.add_trace(go.Scatter(
            x=self.data[x_col],
            y=self.data[col],
            mode='lines+markers',
            name=col
        ))

    fig.update_layout(
        title=title,
        template='plotly_white',
        height=500
    )

    self.plots.append(('time_series', fig, title))

def add_bar_chart(
    self,
    x_col: str,
    y_col: str,
    title: str = "Bar Chart"
):
    """Add bar chart."""
    fig = px.bar(
        self.data,
        x=x_col,
        y=y_col,
        title=title,
        template='plotly_white'
    )

    self.plots.append(('bar', fig, title))

def add_scatter_plot(
    self,
    x_col: str,
    y_col: str,
    color_col: Optional[str] = None,
    title: str = "Scatter Plot"
):
    """Add scatter plot."""
    fig = px.scatter(
        self.data,
        x=x_col,
        y=y_col,
        color=color_col,
        title=title,
        template='plotly_white'
    )

    self.plots.append(('scatter', fig, title))

def add_histogram(
    self,
    column: str,
    title: str = "Distribution"
):
    """Add histogram."""
    fig = px.histogram(
        self.data,
        x=column,
        title=title,
        template='plotly_white'
    )

    self.plots.append(('histogram', fig, title))

def add_heatmap(
    self,
    columns: Optional[List[str]] = None,
    title: str = "Correlation Heatmap"
):
    """Add correlation heatmap."""
    if columns:
        corr = self.data[columns].corr()
    else:
        corr = self.data.select_dtypes(include='number').corr()

    fig = px.imshow(
        corr,
        title=title,
        template='plotly_white',
        color_continuous_scale='RdBu_r'
    )

    self.plots.append(('heatmap', fig, title))

def generate(
    self,
    output_path: Path,
    title: str = "Analysis Report"
) -> Path:
    """
    Generate the HTML report.

    Args:
        output_path: Path to save report
        title: Report title

    Returns:
        Path to generated report
    """
    # Build HTML
    html_parts = [self._generate_header(title)]
    html_parts.append(self._generate_summary_cards())

    for plot_type, fig, plot_title in self.plots:
        html_parts.append(self._wrap_plot(fig, plot_title))

    html_parts.append(self._generate_footer())

    # Combine and save
    html_content = self._get_html_template(
        title,
        '\n'.join(html_parts)
    )

    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(html_content)

    return output_path

def _get_html_template(self, title: str, body: str) -> str:
    """Get complete HTML template."""
    return f"""&#x3C;!DOCTYPE html>

<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{title}</title> <script src="https://cdn.plot.ly/plotly-latest.min.js">&#x3C;/script> <style> {self._get_styles()} </style> </head> <body> {body} </body> </html>"""

def _get_styles(self) -> str:
    """Get CSS styles."""
    return """
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        background: #f5f5f5;
        color: #333;
    }
    .header {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        padding: 40px;
        text-align: center;
    }
    .container { max-width: 1400px; margin: 0 auto; padding: 20px; }
    .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 30px 0; }
    .card { background: white; border-radius: 12px; padding: 25px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); text-align: center; }
    .card-value { font-size: 2.5em; font-weight: bold; color: #667eea; }
    .card-label { color: #666; text-transform: uppercase; font-size: 0.85em; }
    .plot-section { background: white; border-radius: 12px; padding: 25px; margin: 20px 0; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
    .footer { text-align: center; padding: 30px; color: #666; }
    """

def _generate_header(self, title: str) -> str:
    """Generate header HTML."""
    return f"""
    &#x3C;div class="header">
        &#x3C;h1>{title}&#x3C;/h1>
        &#x3C;p>Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}&#x3C;/p>
    &#x3C;/div>
    &#x3C;div class="container">
    """

def _generate_summary_cards(self) -> str:
    """Generate summary cards HTML."""
    cards = ""
    for key, value in self.summary_stats.items():
        label = key.replace('_', ' ').title()
        cards += f"""
        &#x3C;div class="card">
            &#x3C;div class="card-value">{value:,}&#x3C;/div>
            &#x3C;div class="card-label">{label}&#x3C;/div>
        &#x3C;/div>
        """

    return f'&#x3C;div class="cards">{cards}&#x3C;/div>'

def _wrap_plot(self, fig: go.Figure, title: str) -> str:
    """Wrap plot in HTML container."""
    plot_html = fig.to_html(full_html=False, include_plotlyjs=False)
    return f"""
    &#x3C;div class="plot-section">
        &#x3C;h2>{title}&#x3C;/h2>
        {plot_html}
    &#x3C;/div>
    """

def _generate_footer(self) -> str:
    """Generate footer HTML."""
    return """
    &#x3C;/div>
    &#x3C;div class="footer">
        Generated with Interactive Report Generator | workspace-hub
    &#x3C;/div>
    """

Usage Examples

Example 1: Simple Report

import pandas as pd from pathlib import Path from report_generator import InteractiveReportGenerator

Load data

df = pd.read_csv('data/analysis_results.csv')

Create report

report = InteractiveReportGenerator() report.add_data(df) report.add_time_series('date', ['value1', 'value2']) report.add_histogram('value1') report.add_heatmap()

Generate

output = report.generate( Path('reports/analysis_report.html'), title='Analysis Results' ) print(f"Report saved: {output}")

Example 2: Dashboard Creation

Create dashboard with multiple views

report = InteractiveReportGenerator() report.add_data(df)

Add various plots

report.add_time_series('timestamp', ['metric_a', 'metric_b']) report.add_scatter_plot('x_value', 'y_value', color_col='category') report.add_bar_chart('category', 'total') report.add_histogram('distribution_col')

Generate dashboard

report.generate( Path('reports/dashboard.html'), title='Project Dashboard' )

Example 3: From CLI

Generate report from command line

python -m report_generator
--data results.csv
--output report.html
--title "Analysis Results"
--plots time_series histogram heatmap

Execution Checklist

Data Preparation:

  • Data loaded as DataFrame

  • Column names are clean

  • Date columns parsed

  • Missing values handled

Report Creation:

  • Add data to generator

  • Select appropriate plot types

  • Configure titles and labels

  • Generate HTML output

Validation:

  • Open report in browser

  • Verify interactivity works

  • Check responsive design

  • Test export options

Best Practices

  • Use descriptive titles - Help users understand each visualization

  • Limit plots per page - 5-7 plots maximum for clarity

  • Use consistent colors - Match organizational branding

  • Include summary stats - Provide context for data

  • Test on mobile - Verify responsive design works

Error Handling

Missing Columns

Error: Column 'value' not found in data

Check:

  1. Column names in DataFrame
  2. Spelling and case sensitivity
  3. Data was loaded correctly

Empty Data

Error: No data to plot

Check:

  1. DataFrame is not empty
  2. Filters not too restrictive
  3. Data loading succeeded

Related Skills

  • data-validation-reporter - Data quality reports

  • yaml-workflow-executor - Configuration-driven reports

References

  • Plotly Python Documentation

  • HTML Reporting Standards

Version History

  • 1.0.0 (2026-01-14): Initial release - Plotly-based interactive HTML report generator with dashboards, multiple plot types, and responsive design

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

echarts

No summary provided by upstream source.

Repository SourceNeeds Review
General

pandoc

No summary provided by upstream source.

Repository SourceNeeds Review
General

mkdocs

No summary provided by upstream source.

Repository SourceNeeds Review
General

gis

No summary provided by upstream source.

Repository SourceNeeds Review