"""Pry — Automated White-Label Report Generator.
Generate branded PDF/HTML reports from scraped data for client delivery."""
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 Rug Munch Media LLC
#
# Part of Pry — https://git.rugmunch.io/RugMunchMedia/pryscraper
# Licensed under MIT. See LICENSE.
import logging
import os
import uuid
from datetime import UTC, datetime, timedelta
from typing import Any
from paths import PRY_DATA_DIR
logger = logging.getLogger(__name__)
REPORTS_DIR = PRY_DATA_DIR / "reports"
REPORTS_DIR.mkdir(parents=True, exist_ok=True)
def generate_report(
report_type: str,
data: dict[str, Any],
branding: dict[str, Any] | None = None,
output_format: str = "html",
) -> dict[str, Any]:
"""Generate a white-label report from scraped data.
Args:
report_type: competitive_analysis, price_monitor, seo_audit, content_tracker
data: The scraped data to include in the report
branding: Custom branding (agency_name, logo_url, brand_color, domain)
output_format: html or pdf
Returns report metadata and file path.
"""
report_id = uuid.uuid4().hex[:8]
brand = branding or {}
agency_name = brand.get("agency_name", "Pry Intelligence")
brand_color = brand.get("brand_color", "#f59e0b")
logo_url = brand.get("logo_url", "")
now = datetime.now(UTC)
report_date = now.strftime("%B %d, %Y")
period_start = (now - timedelta(days=7)).strftime("%b %d")
period_end = now.strftime("%b %d, %Y")
if report_type == "competitive_analysis":
html = _build_competitive_report(
data, agency_name, brand_color, logo_url, report_date, period_start, period_end
)
elif report_type == "price_monitor":
html = _build_price_report(
data, agency_name, brand_color, logo_url, report_date, period_start, period_end
)
elif report_type == "seo_audit":
html = _build_seo_report(data, agency_name, brand_color, logo_url, report_date)
elif report_type == "content_tracker":
html = _build_content_report(
data, agency_name, brand_color, logo_url, report_date, period_start, period_end
)
else:
return {"error": f"Unknown report type: {report_type}"}
# Save HTML report
report_path = REPORTS_DIR / f"{report_id}_{report_type}.html"
try:
report_path.write_text(html)
except OSError as e:
return {"error": str(e)}
return {
"success": True,
"report_id": report_id,
"type": report_type,
"format": "html",
"path": str(report_path),
"title": f"{agency_name} — {report_type.replace('_', ' ').title()}",
"generated_at": datetime.now(UTC).isoformat(),
}
def _build_html_base(
title: str, body: str, brand_color: str, logo_url: str, agency_name: str
) -> str:
"""Build HTML document with white-label branding."""
logo_html = (
f''
if logo_url
else f'
Tracking {len(competitors)} competitors across your market.
| Competitor | Price | Change | Last Updated |
|---|
No significant changes detected this period.
'}Products Tracked
Price Drops
Price Rises
| Product | Previous | Current | Change |
|---|
| Title | {seo.get("title", "N/A")[:100]} |
| Meta Description | {seo.get("meta_description", "N/A")[:150]} |
| Word Count | {seo.get("word_count", 0):,} |
| Internal Links | {seo.get("links_internal", 0)} |
| External Links | {seo.get("links_external", 0)} |
| Schema Markup | {"✅ Present" if seo.get("has_schema") else "❌ Not found"} |
No changes detected since last scan.
'}Monitoring {len(pages)} pages for content changes.
| Status | Page | Changed | Words |
|---|
No content changes detected this period.
'}