Spaces:
Build error
Build error
| #!/usr/bin/env python3 | |
| """ | |
| Generate comprehensive launch readiness report | |
| Combines all verification checks into a single PDF/HTML report | |
| """ | |
| import asyncio | |
| import json | |
| import subprocess | |
| import sys | |
| from datetime import datetime | |
| from pathlib import Path | |
| from typing import Dict, List, Any | |
| try: | |
| from jinja2 import Template | |
| import markdown | |
| except ImportError: | |
| print("Installing required packages...") | |
| subprocess.run([sys.executable, "-m", "pip", "install", "jinja2", "markdown"], check=True) | |
| from jinja2 import Template | |
| import markdown | |
| HTML_TEMPLATE = """ | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AudioForge Launch Report - {{ timestamp }}</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; | |
| line-height: 1.6; | |
| color: #333; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 20px 60px rgba(0,0,0,0.3); | |
| overflow: hidden; | |
| } | |
| .header { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 40px; | |
| text-align: center; | |
| } | |
| .header h1 { | |
| font-size: 2.5em; | |
| margin-bottom: 10px; | |
| font-weight: 700; | |
| } | |
| .header .subtitle { | |
| font-size: 1.2em; | |
| opacity: 0.9; | |
| } | |
| .header .timestamp { | |
| margin-top: 20px; | |
| font-size: 0.9em; | |
| opacity: 0.8; | |
| } | |
| .summary { | |
| padding: 40px; | |
| background: #f8f9fa; | |
| border-bottom: 2px solid #e9ecef; | |
| } | |
| .summary-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 20px; | |
| margin-top: 20px; | |
| } | |
| .summary-card { | |
| background: white; | |
| padding: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 8px rgba(0,0,0,0.1); | |
| text-align: center; | |
| transition: transform 0.2s; | |
| } | |
| .summary-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.15); | |
| } | |
| .summary-card .value { | |
| font-size: 2.5em; | |
| font-weight: 700; | |
| margin: 10px 0; | |
| } | |
| .summary-card .label { | |
| color: #666; | |
| font-size: 0.9em; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } | |
| .summary-card.success .value { color: #28a745; } | |
| .summary-card.warning .value { color: #ffc107; } | |
| .summary-card.danger .value { color: #dc3545; } | |
| .summary-card.info .value { color: #17a2b8; } | |
| .content { | |
| padding: 40px; | |
| } | |
| .section { | |
| margin-bottom: 40px; | |
| } | |
| .section-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| padding-bottom: 15px; | |
| border-bottom: 2px solid #e9ecef; | |
| } | |
| .section-header h2 { | |
| font-size: 1.8em; | |
| color: #667eea; | |
| } | |
| .section-badge { | |
| padding: 5px 15px; | |
| border-radius: 20px; | |
| font-size: 0.8em; | |
| font-weight: 600; | |
| } | |
| .section-badge.pass { | |
| background: #d4edda; | |
| color: #155724; | |
| } | |
| .section-badge.fail { | |
| background: #f8d7da; | |
| color: #721c24; | |
| } | |
| .section-badge.warn { | |
| background: #fff3cd; | |
| color: #856404; | |
| } | |
| .checks-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| margin-top: 20px; | |
| } | |
| .checks-table th { | |
| background: #f8f9fa; | |
| padding: 12px; | |
| text-align: left; | |
| font-weight: 600; | |
| border-bottom: 2px solid #dee2e6; | |
| } | |
| .checks-table td { | |
| padding: 12px; | |
| border-bottom: 1px solid #e9ecef; | |
| } | |
| .checks-table tr:hover { | |
| background: #f8f9fa; | |
| } | |
| .status-icon { | |
| font-size: 1.5em; | |
| display: inline-block; | |
| width: 30px; | |
| text-align: center; | |
| } | |
| .check-details { | |
| font-size: 0.9em; | |
| color: #666; | |
| margin-top: 5px; | |
| } | |
| .fix-command { | |
| background: #f8f9fa; | |
| padding: 8px 12px; | |
| border-radius: 4px; | |
| font-family: 'Courier New', monospace; | |
| font-size: 0.85em; | |
| margin-top: 5px; | |
| border-left: 3px solid #ffc107; | |
| } | |
| .footer { | |
| background: #f8f9fa; | |
| padding: 30px; | |
| text-align: center; | |
| color: #666; | |
| border-top: 2px solid #e9ecef; | |
| } | |
| .footer .logo { | |
| font-size: 2em; | |
| margin-bottom: 10px; | |
| } | |
| .progress-bar { | |
| width: 100%; | |
| height: 30px; | |
| background: #e9ecef; | |
| border-radius: 15px; | |
| overflow: hidden; | |
| margin: 20px 0; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, #28a745 0%, #20c997 100%); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: white; | |
| font-weight: 600; | |
| transition: width 0.3s ease; | |
| } | |
| .action-items { | |
| background: #fff3cd; | |
| border-left: 4px solid #ffc107; | |
| padding: 20px; | |
| margin: 20px 0; | |
| border-radius: 4px; | |
| } | |
| .action-items h3 { | |
| color: #856404; | |
| margin-bottom: 15px; | |
| } | |
| .action-items ul { | |
| list-style: none; | |
| padding-left: 0; | |
| } | |
| .action-items li { | |
| padding: 8px 0; | |
| border-bottom: 1px solid #ffeaa7; | |
| } | |
| .action-items li:last-child { | |
| border-bottom: none; | |
| } | |
| @media print { | |
| body { | |
| background: white; | |
| padding: 0; | |
| } | |
| .container { | |
| box-shadow: none; | |
| } | |
| .summary-card:hover { | |
| transform: none; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>🎵 AudioForge Launch Report</h1> | |
| <div class="subtitle">Production Readiness Verification</div> | |
| <div class="timestamp">Generated: {{ timestamp }}</div> | |
| </div> | |
| <div class="summary"> | |
| <h2>Executive Summary</h2> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" style="width: {{ overall_success_rate }}%;"> | |
| {{ overall_success_rate }}% Ready | |
| </div> | |
| </div> | |
| <div class="summary-grid"> | |
| <div class="summary-card success"> | |
| <div class="label">Passed</div> | |
| <div class="value">{{ total_passed }}</div> | |
| </div> | |
| <div class="summary-card danger"> | |
| <div class="label">Failed</div> | |
| <div class="value">{{ total_failed }}</div> | |
| </div> | |
| <div class="summary-card warning"> | |
| <div class="label">Warnings</div> | |
| <div class="value">{{ total_warned }}</div> | |
| </div> | |
| <div class="summary-card info"> | |
| <div class="label">Total Checks</div> | |
| <div class="value">{{ total_checks }}</div> | |
| </div> | |
| </div> | |
| {% if overall_success_rate == 100 %} | |
| <div style="margin-top: 30px; padding: 20px; background: #d4edda; border-radius: 8px; text-align: center;"> | |
| <h3 style="color: #155724; font-size: 1.5em;">🎉 READY TO LAUNCH!</h3> | |
| <p style="color: #155724; margin-top: 10px;">All systems are go. You're cleared for production deployment.</p> | |
| </div> | |
| {% elif overall_success_rate >= 90 %} | |
| <div style="margin-top: 30px; padding: 20px; background: #fff3cd; border-radius: 8px; text-align: center;"> | |
| <h3 style="color: #856404; font-size: 1.5em;">⚠️ ALMOST READY</h3> | |
| <p style="color: #856404; margin-top: 10px;">Minor issues to address before launch.</p> | |
| </div> | |
| {% else %} | |
| <div style="margin-top: 30px; padding: 20px; background: #f8d7da; border-radius: 8px; text-align: center;"> | |
| <h3 style="color: #721c24; font-size: 1.5em;">❌ NOT READY</h3> | |
| <p style="color: #721c24; margin-top: 10px;">Critical issues must be resolved before launch.</p> | |
| </div> | |
| {% endif %} | |
| </div> | |
| <div class="content"> | |
| {% for section_name, section_data in sections.items() %} | |
| <div class="section"> | |
| <div class="section-header"> | |
| <h2>{{ section_name }}</h2> | |
| {% if section_data.success_rate == 100 %} | |
| <span class="section-badge pass">✅ {{ section_data.passed }}/{{ section_data.total }}</span> | |
| {% elif section_data.failed > 0 %} | |
| <span class="section-badge fail">❌ {{ section_data.failed }} Failed</span> | |
| {% else %} | |
| <span class="section-badge warn">⚠️ {{ section_data.warned }} Warnings</span> | |
| {% endif %} | |
| </div> | |
| <table class="checks-table"> | |
| <thead> | |
| <tr> | |
| <th style="width: 50px;">Status</th> | |
| <th>Check</th> | |
| <th>Message</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for check in section_data.checks %} | |
| <tr> | |
| <td><span class="status-icon">{{ check.status_icon }}</span></td> | |
| <td><strong>{{ check.name }}</strong></td> | |
| <td> | |
| {{ check.message }} | |
| {% if check.details %} | |
| <div class="check-details">{{ check.details }}</div> | |
| {% endif %} | |
| {% if check.fix_command %} | |
| <div class="fix-command">Fix: {{ check.fix_command }}</div> | |
| {% endif %} | |
| </td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| {% endfor %} | |
| {% if action_items %} | |
| <div class="action-items"> | |
| <h3>⚡ Action Items</h3> | |
| <ul> | |
| {% for item in action_items %} | |
| <li>{{ item }}</li> | |
| {% endfor %} | |
| </ul> | |
| </div> | |
| {% endif %} | |
| </div> | |
| <div class="footer"> | |
| <div class="logo">🐼⚡</div> | |
| <p>AudioForge Launch Verification System</p> | |
| <p style="margin-top: 10px; font-size: 0.9em;"> | |
| Forged by FusionPanda | {{ timestamp }} | |
| </p> | |
| </div> | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| async def generate_report(): | |
| """Generate comprehensive launch report.""" | |
| print("🎵 Generating AudioForge Launch Report...") | |
| print("=" * 60) | |
| # Run verification script | |
| print("\n📊 Running verification checks...") | |
| result = subprocess.run( | |
| [sys.executable, "scripts/launch_verification.py", "--json", "launch-results.json"], | |
| capture_output=True, | |
| text=True | |
| ) | |
| # Load results | |
| results_file = Path("launch-results.json") | |
| if not results_file.exists(): | |
| print("❌ Verification results not found!") | |
| sys.exit(1) | |
| with open(results_file) as f: | |
| data = json.load(f) | |
| # Calculate totals | |
| total_passed = sum(section["passed"] for section in data.values()) | |
| total_failed = sum(section["failed"] for section in data.values()) | |
| total_warned = sum(section["warned"] for section in data.values()) | |
| total_checks = sum(section["total"] for section in data.values()) | |
| overall_success_rate = round((total_passed / total_checks * 100) if total_checks > 0 else 0, 1) | |
| # Collect action items | |
| action_items = [] | |
| for section_name, section_data in data.items(): | |
| for check in section_data["checks"]: | |
| if check.get("details") and ("fix" in check["details"].lower() or "missing" in check["details"].lower()): | |
| action_items.append(f"{section_name}: {check['name']} - {check['message']}") | |
| # Prepare template data | |
| template_data = { | |
| "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
| "total_passed": total_passed, | |
| "total_failed": total_failed, | |
| "total_warned": total_warned, | |
| "total_checks": total_checks, | |
| "overall_success_rate": overall_success_rate, | |
| "sections": data, | |
| "action_items": action_items[:10], # Top 10 action items | |
| } | |
| # Add status icons | |
| status_icons = { | |
| "PASS": "✅", | |
| "FAIL": "❌", | |
| "WARN": "⚠️", | |
| "SKIP": "⏭️", | |
| "INFO": "ℹ️", | |
| } | |
| for section_data in template_data["sections"].values(): | |
| for check in section_data["checks"]: | |
| check["status_icon"] = status_icons.get(check["status"], "❓") | |
| # Generate HTML report | |
| template = Template(HTML_TEMPLATE) | |
| html_content = template.render(**template_data) | |
| # Save report | |
| report_path = Path("LAUNCH_REPORT.html") | |
| report_path.write_text(html_content, encoding="utf-8") | |
| print(f"\n✅ Report generated: {report_path.absolute()}") | |
| print(f"📊 Overall Success Rate: {overall_success_rate}%") | |
| print(f"✅ Passed: {total_passed}") | |
| print(f"❌ Failed: {total_failed}") | |
| print(f"⚠️ Warnings: {total_warned}") | |
| # Print status | |
| if overall_success_rate == 100: | |
| print("\n🎉 READY TO LAUNCH!") | |
| elif overall_success_rate >= 90: | |
| print("\n⚠️ ALMOST READY - Minor issues to fix") | |
| else: | |
| print("\n❌ NOT READY - Critical issues found") | |
| # Open report in browser (optional) | |
| try: | |
| import webbrowser | |
| webbrowser.open(f"file://{report_path.absolute()}") | |
| print(f"\n🌐 Opening report in browser...") | |
| except Exception: | |
| pass | |
| return overall_success_rate >= 90 | |
| if __name__ == "__main__": | |
| success = asyncio.run(generate_report()) | |
| sys.exit(0 if success else 1) | |