Report Generation - rotalabs-comply
Description: AI compliance and audit logging infrastructure

Skip to content

# Report Generation Tutorial

This tutorial covers generating and exporting compliance reports from audit data, including customization and scheduling for automated reporting.

## Overview

Compliance reports summarize audit data and compliance check results for stakeholders:

- Executive summaries for leadership
- Risk assessments for security teams
- Compliance matrices for auditors
- Recommendations for operations teams

## Basic Report Generation

### Setup

    import asyncio
    from datetime import datetime, timedelta
    from rotalabs_comply import AuditLogger, ReportGenerator
    from rotalabs_comply.audit import MemoryStorage
    from rotalabs_comply.frameworks.base import ComplianceProfile
    from rotalabs_comply.frameworks.eu_ai_act import EUAIActFramework
    from rotalabs_comply.frameworks.soc2 import SOC2Framework
    from rotalabs_comply.frameworks.hipaa import HIPAAFramework

    # Set up storage and logger
    storage = MemoryStorage()
    logger = AuditLogger(storage)

    # Create report generator with frameworks
    generator = ReportGenerator(
        audit_logger=storage,
        frameworks={
            "eu_ai_act": EUAIActFramework(),
            "soc2": SOC2Framework(),
            "hipaa": HIPAAFramework(),
        },
    )

    # Create compliance profile
    profile = ComplianceProfile(
        profile_id="production",
        name="Production AI System",
        enabled_frameworks=["eu_ai_act", "soc2"],
    )

### Generate Full Report

    async def main():
        # First, log some entries
        for i in range(100):
            await logger.log(
                input=f"Query {i}",
                output=f"Response {i}",
                provider="openai",
                model="gpt-4",
                safety_passed=i % 10 != 0,  # 10% failure rate
                latency_ms=100 + i,
            )

        # Define reporting period
        end = datetime.utcnow()
        start = end - timedelta(days=30)

        # Generate report
        report = await generator.generate(
            period_start=start,
            period_end=end,
            profile=profile,
        )

        print(f"Report: {report.title}")
        print(f"ID: {report.id}")
        print(f"Period: {report.period_start} to {report.period_end}")
        print(f"Entries analyzed: {report.total_entries}")
        print(f"Violations found: {report.violations_count}")
        print(f"Compliance score: {report.compliance_score:.2%}")
        print(f"Status: {report.status}")

    asyncio.run(main())

## Export Formats

### Markdown Export

Perfect for documentation systems and README files:

    # Export to Markdown
    markdown = generator.export_markdown(report)

    # Save to file
    with open("compliance_report.md", "w") as f:
        f.write(markdown)

    # Preview
    print(markdown[:1000])

Output structure:

    # Compliance Report - Production AI System

    **Report ID:** abc-123-def
    **Generated:** 2026-01-29 10:30:00 UTC
    **Period:** 2026-01-01 to 2026-01-29
    **Framework:** Multiple
    **Profile:** Production AI System

    ---

    **Compliance Score:** 95.50%
    **Status:** COMPLIANT
    **Total Entries:** 10,000
    **Violations:** 45

    ---

    ## Executive Summary

    **Compliance Status: COMPLIANT**
    ...

### JSON Export

For programmatic processing and API integration:

    import json

    # Export to JSON
    json_str = generator.export_json(report)

    # Save to file
    with open("compliance_report.json", "w") as f:
        f.write(json_str)

    # Load and process
    data = json.loads(json_str)
    print(f"Score: {data['compliance_score']}")
    print(f"Sections: {len(data['sections'])}")

### HTML Export

Standalone reports for stakeholders:

    # Export to HTML
    html = generator.export_html(report)

    # Save to file
    with open("compliance_report.html", "w") as f:
        f.write(html)

    # Open in browser (optional)
    import webbrowser
    webbrowser.open("compliance_report.html")

The HTML export includes: - Embedded CSS styling - Color-coded status badges - Responsive layout - Tables with alternating row colors - Professional formatting

## Report Sections

### Executive Summary

High-level overview for leadership:

    # Access executive summary section
    for section in report.sections:
        if section.title == "Executive Summary":
            print(section.content)
            print(f"Status: {section.metadata.get('status')}")
            print(f"Rate: {section.metadata.get('compliance_rate')}%")

Content includes: - Overall compliance status - Key metrics table - Frameworks evaluated - Summary interpretation

### Risk Assessment

Security-focused analysis:

    for section in report.sections:
        if section.title == "Risk Assessment":
            print(f"Overall risk: {section.metadata.get('overall_risk')}")
            print(f"Severity counts: {section.metadata.get('severity_counts')}")

Content includes: - Overall risk level - Severity distribution table - Findings by category - Priority findings list - Risk-based recommendations

### Compliance Matrix

Auditor-friendly rule-by-rule breakdown:

    for section in report.sections:
        if section.title == "Compliance Matrix":
            print(f"Frameworks: {section.metadata.get('frameworks')}")
            print(f"Total checks: {section.metadata.get('total_checks')}")
            print(f"Overall rate: {section.metadata.get('compliance_rate')}%")

Content includes: - Framework summary table - Pass/fail counts per framework - Detailed violation list - Overall statistics

### Metrics Summary

Performance and safety metrics:

    for section in report.sections:
        if section.title == "Metrics Summary":
            print(f"Safety rate: {section.metadata.get('safety_rate')}%")
            print(f"Avg latency: {section.metadata.get('avg_latency')}ms")
            print(f"P95 latency: {section.metadata.get('p95_latency')}ms")

Content includes: - Volume metrics - Safety pass rates - Latency percentiles (P50, P95, P99) - Provider distribution

### Recommendations

Prioritized action items:

    for section in report.sections:
        if section.title == "Recommendations":
            print(f"Total: {section.metadata.get('recommendation_count')}")
            print(f"Immediate: {section.metadata.get('immediate_count')}")
            print(f"Short-term: {section.metadata.get('short_term_count')}")

Content includes: - Immediate actions (24-48 hours) - Short-term actions (1-2 weeks) - Long-term improvements - General best practices

### Audit Summary

Volume and activity analysis:

    for section in report.sections:
        if section.title == "Audit Summary":
            print(f"Period: {section.metadata.get('period')}")
            print(f"Daily average: {section.metadata.get('avg_daily')}")
            print(f"Peak volume: {section.metadata.get('peak_daily')}")

Content includes: - Volume summary - Peak activity days - Failure patterns - Retention notes

## Framework-Specific Reports

### EU AI Act Report

    report = await generator.generate(
        period_start=start,
        period_end=end,
        profile=profile,
        framework="eu_ai_act",
    )

    print(report.title)  # "EU AI Act Compliance Report"

Template includes sections for: - Risk classification - Transparency obligations - Human oversight - Data governance - Technical documentation

### SOC2 Report

    report = await generator.generate(
        period_start=start,
        period_end=end,
        profile=profile,
        framework="soc2",
    )

    print(report.title)  # "SOC2 Type II Compliance Report"

Template includes sections for: - System overview - Security controls (CC) - Availability controls (A) - Processing integrity (PI) - Confidentiality controls (C) - Privacy controls (P)

### HIPAA Report

    report = await generator.generate(
        period_start=start,
        period_end=end,
        profile=profile,
        framework="hipaa",
    )

    print(report.title)  # "HIPAA Compliance Report"

Template includes sections for: - Administrative safeguards - Physical safeguards - Technical safeguards - Breach notification - PHI handling

## Executive Summary Report

Generate a condensed report for executives:

    report = await generator.generate_executive_summary(
        period_start=start,
        period_end=end,
        profile=profile,
    )

    print(f"Title: {report.title}")  # "Executive Summary - Production AI System"
    print(f"Sections: {len(report.sections)}")  # Fewer sections

Executive summary includes only: - Executive summary - Risk assessment - Recommendations

## Scheduled Reporting

### Daily Reports

    import asyncio
    from datetime import datetime, timedelta

    async def generate_daily_report():
        end = datetime.utcnow().replace(hour=0, minute=0, second=0)
        start = end - timedelta(days=1)

        report = await generator.generate(
            period_start=start,
            period_end=end,
            profile=profile,
        )

        # Save reports
        date_str = start.strftime("%Y%m%d")
        generator.export_markdown(report)
        with open(f"reports/daily_{date_str}.md", "w") as f:
            f.write(generator.export_markdown(report))

        return report

    async def daily_report_scheduler():
        while True:
            now = datetime.now()
            # Run at 6 AM
            next_run = now.replace(hour=6, minute=0, second=0)
            if next_run <= now:
                next_run += timedelta(days=1)

            await asyncio.sleep((next_run - now).total_seconds())

            try:
                report = await generate_daily_report()
                print(f"[{datetime.now()}] Generated daily report: {report.status}")
            except Exception as e:
                print(f"[{datetime.now()}] Report generation failed: {e}")

### Weekly Reports

    async def generate_weekly_report():
        end = datetime.utcnow()
        start = end - timedelta(days=7)

        report = await generator.generate(
            period_start=start,
            period_end=end,
            profile=profile,
        )

        # Generate all formats
        week_str = start.strftime("%Y-W%V")

        with open(f"reports/weekly_{week_str}.md", "w") as f:
            f.write(generator.export_markdown(report))

        with open(f"reports/weekly_{week_str}.html", "w") as f:
            f.write(generator.export_html(report))

        with open(f"reports/weekly_{week_str}.json", "w") as f:
            f.write(generator.export_json(report))

        return report

### Monthly Reports

    import calendar

    async def generate_monthly_report(year: int, month: int):
        # Calculate month boundaries
        _, last_day = calendar.monthrange(year, month)
        start = datetime(year, month, 1)
        end = datetime(year, month, last_day, 23, 59, 59)

        report = await generator.generate(
            period_start=start,
            period_end=end,
            profile=profile,
        )

        month_str = start.strftime("%Y-%m")

        with open(f"reports/monthly_{month_str}.html", "w") as f:
            f.write(generator.export_html(report))

        return report

### Quarterly Reports

    async def generate_quarterly_report(year: int, quarter: int):
        # Q1: Jan-Mar, Q2: Apr-Jun, Q3: Jul-Sep, Q4: Oct-Dec
        start_month = (quarter - 1) * 3 + 1
        end_month = start_month + 2
        _, last_day = calendar.monthrange(year, end_month)

        start = datetime(year, start_month, 1)
        end = datetime(year, end_month, last_day, 23, 59, 59)

        report = await generator.generate(
            period_start=start,
            period_end=end,
            profile=profile,
        )

        quarter_str = f"{year}-Q{quarter}"

        with open(f"reports/quarterly_{quarter_str}.html", "w") as f:
            f.write(generator.export_html(report))

        return report

## Email Distribution

### Send Report via Email

    import smtplib
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    from email.mime.application import MIMEApplication

    async def send_compliance_report(report, recipients):
        # Generate HTML report
        html_content = generator.export_html(report)

        # Create email
        msg = MIMEMultipart()
        msg["Subject"] = f"Compliance Report: {report.title} - {report.status.upper()}"
        msg["From"] = "compliance@company.com"
        msg["To"] = ", ".join(recipients)

        # Add summary in body
        body = f"""
        Compliance Report Summary
        ========================
        Period: {report.period_start.strftime('%Y-%m-%d')} to {report.period_end.strftime('%Y-%m-%d')}
        Status: {report.status.upper()}
        Compliance Score: {report.compliance_score:.2%}
        Entries Analyzed: {report.total_entries:,}
        Violations Found: {report.violations_count:,}

        Full report attached.
        """
        msg.attach(MIMEText(body, "plain"))

        # Attach HTML report
        attachment = MIMEApplication(html_content.encode(), Name="compliance_report.html")
        attachment["Content-Disposition"] = 'attachment; filename="compliance_report.html"'
        msg.attach(attachment)

        # Send
        with smtplib.SMTP("smtp.company.com", 587) as server:
            server.starttls()
            server.login("compliance@company.com", "password")
            server.send_message(msg)

        print(f"Report sent to {recipients}")

## Slack Integration

### Post Report Summary

    import httpx

    async def post_to_slack(report, webhook_url):
        # Determine emoji based on status
        emoji = {
            "compliant": ":white_check_mark:",
            "needs_review": ":warning:",
            "non_compliant": ":x:",
        }.get(report.status, ":question:")

        # Create message
        message = {
            "text": f"{emoji} Compliance Report Generated",
            "blocks": [
                {
                    "type": "header",
                    "text": {
                        "type": "plain_text",
                        "text": f"{emoji} {report.title}",
                    },
                },
                {
                    "type": "section",
                    "fields": [
                        {
                            "type": "mrkdwn",
                            "text": f"*Status:*\n{report.status.upper()}",
                        },
                        {
                            "type": "mrkdwn",
                            "text": f"*Score:*\n{report.compliance_score:.2%}",
                        },
                        {
                            "type": "mrkdwn",
                            "text": f"*Entries:*\n{report.total_entries:,}",
                        },
                        {
                            "type": "mrkdwn",
                            "text": f"*Violations:*\n{report.violations_count:,}",
                        },
                    ],
                },
                {
                    "type": "section",
                    "text": {
                        "type": "mrkdwn",
                        "text": f"Period: {report.period_start.strftime('%Y-%m-%d')} to {report.period_end.strftime('%Y-%m-%d')}",
                    },
                },
            ],
        }

        # Send to Slack
        async with httpx.AsyncClient() as client:
            response = await client.post(webhook_url, json=message)
            response.raise_for_status()

## Custom Report Templates

### Using Pre-defined Templates

    from rotalabs_comply.reports.templates import (
        EU_AI_ACT_TEMPLATE,
        SOC2_TEMPLATE,
        HIPAA_TEMPLATE,
        EXECUTIVE_SUMMARY_TEMPLATE,
    )

    print(EU_AI_ACT_TEMPLATE.title)     # "EU AI Act Compliance Report"
    print(EU_AI_ACT_TEMPLATE.sections)  # List of section names
    print(EU_AI_ACT_TEMPLATE.format)    # "markdown"

### Creating Custom Templates

    from rotalabs_comply.reports.templates import ReportTemplate

    custom_template = ReportTemplate(
        framework="custom",
        title="Custom Compliance Report",
        sections=[
            "executive_summary",
            "risk_assessment",
            "custom_section",
            "recommendations",
        ],
        format="html",
    )

## Best Practices

### 1. Archive Reports

    import os
    from datetime import datetime

    def archive_report(report, base_path="/var/reports"):
        # Create archive structure
        year = report.period_end.strftime("%Y")
        month = report.period_end.strftime("%m")
        archive_dir = os.path.join(base_path, year, month)
        os.makedirs(archive_dir, exist_ok=True)

        # Save in all formats
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        base_name = f"compliance_{timestamp}"

        with open(os.path.join(archive_dir, f"{base_name}.md"), "w") as f:
            f.write(generator.export_markdown(report))

        with open(os.path.join(archive_dir, f"{base_name}.html"), "w") as f:
            f.write(generator.export_html(report))

        with open(os.path.join(archive_dir, f"{base_name}.json"), "w") as f:
            f.write(generator.export_json(report))

        return archive_dir

### 2. Track Report Metrics

    report_metrics = []

    async def generate_and_track(period_start, period_end, profile):
        report = await generator.generate(
            period_start=period_start,
            period_end=period_end,
            profile=profile,
        )

        # Track metrics over time
        report_metrics.append({
            "generated_at": datetime.now().isoformat(),
            "period": f"{period_start.date()} to {period_end.date()}",
            "score": report.compliance_score,
            "status": report.status,
            "violations": report.violations_count,
            "entries": report.total_entries,
        })

        return report

    # Analyze trends
    import pandas as pd
    df = pd.DataFrame(report_metrics)
    print(df.describe())

### 3. Alert on Critical Status

    async def generate_with_alerts(period_start, period_end, profile):
        report = await generator.generate(
            period_start=period_start,
            period_end=period_end,
            profile=profile,
        )

        # Alert on non-compliant status
        if report.status == "non_compliant":
            # Send immediate alert
            await send_alert(
                level="critical",
                message=f"Non-compliant report generated: {report.violations_count} violations",
                report_id=report.id,
            )

        # Alert on score drops
        previous_score = get_previous_score()
        if previous_score and report.compliance_score < previous_score - 0.05:
            await send_alert(
                level="warning",
                message=f"Compliance score dropped from {previous_score:.2%} to {report.compliance_score:.2%}",
                report_id=report.id,
            )

        return report