"""Report generator for the CloudFormation Stack Updater.""" from __future__ import annotations from datetime import datetime from cfn_updater.models import StackUpdateResult, UpdateRunReport def generate_report( results: list[StackUpdateResult], total_found: int, start_time: datetime, end_time: datetime, ) -> UpdateRunReport: """Aggregate results into a summary report.""" succeeded = sum(1 for r in results if r.status == "succeeded") failed = sum(1 for r in results if r.status == "failed") skipped = sum(1 for r in results if r.status == "skipped") no_update_needed = sum(1 for r in results if r.status == "no-update-needed") return UpdateRunReport( start_time=start_time, end_time=end_time, total_found=total_found, succeeded=succeeded, failed=failed, skipped=skipped, no_update_needed=no_update_needed, results=list(results), ) def format_report(report: UpdateRunReport) -> str: """Format the report as a human-readable string for console output.""" lines: list[str] = [] lines.append("=" * 60) lines.append("CloudFormation Stack Update Report") lines.append("=" * 60) lines.append(f"Start Time : {report.start_time.isoformat()}") lines.append(f"End Time : {report.end_time.isoformat()}") lines.append(f"Total Found: {report.total_found}") lines.append("") lines.append("Per-Stack Results:") lines.append("-" * 60) for r in report.results: line = f" {r.stack_name}: {r.status} ({r.duration_seconds:.1f}s)" if r.error: line += f" - {r.error}" lines.append(line) lines.append("") lines.append("Summary:") lines.append("-" * 60) lines.append(f" Succeeded : {report.succeeded}") lines.append(f" Failed : {report.failed}") lines.append(f" Skipped : {report.skipped}") lines.append(f" No Update Needed: {report.no_update_needed}") lines.append("=" * 60) return "\n".join(lines) def get_exit_code(report: UpdateRunReport) -> int: """Return 0 if no failures, 1 if any stack failed.""" return 1 if report.failed > 0 else 0