diff --git a/src/py_dvt_ate/app/cli.py b/src/py_dvt_ate/app/cli.py index b13fb63..8259ad6 100644 --- a/src/py_dvt_ate/app/cli.py +++ b/src/py_dvt_ate/app/cli.py @@ -208,6 +208,127 @@ def list_runs_cmd( repo.close() +@app.command(name="export-report") +def export_report_cmd( + run_id: Annotated[ + str, + typer.Argument(help="Test run ID (short 8-char or full UUID)."), + ], + output: Annotated[ + str | None, + typer.Option("--output", "-o", help="Output PDF file path."), + ] = None, + company: Annotated[ + str | None, + typer.Option("--company", help="Company name for report header."), + ] = None, + config_file: Annotated[ + str | None, + typer.Option("--config", "-c", help="Path to configuration YAML file."), + ] = None, +) -> None: + """Export a PDF report for a test run. + + Generate a professional PDF report from test results. The run_id can be + the short 8-character ID shown by list-runs, or the full UUID. + + Examples: + py-dvt-ate export-report abc12345 + py-dvt-ate export-report abc12345 --output ./my_report.pdf + py-dvt-ate export-report abc12345 --company "My Company" + """ + from pathlib import Path + from uuid import UUID + + from rich.console import Console + + from py_dvt_ate.app.config import load_config + from py_dvt_ate.data.repository import SQLiteRepository + + console = Console() + + # Check for reporting dependencies + try: + from py_dvt_ate.reporting import ReportConfig, ReportGenerator + except ImportError: + console.print( + "[red]Error:[/red] Report generation requires additional dependencies.\n" + "Install with: [cyan]pip install py_dvt_ate[reports][/cyan]" + ) + raise typer.Exit(1) + + # Load config + if config_file is None: + config_path = Path("config/default.yaml") + if config_path.exists(): + config_file = str(config_path) + + config = load_config(config_file) + + # Create repository + repo = SQLiteRepository( + db_path=config.data.database_path, + measurements_dir=config.data.measurements_dir, + ) + + try: + # Resolve short ID to full UUID + full_run_id: UUID | None = None + + if len(run_id) == 8: + # Short ID - need to find full UUID + all_runs = repo.get_all_runs() + matching_runs = [r for r in all_runs if r.id.startswith(run_id)] + + if not matching_runs: + console.print(f"[red]Error:[/red] No test run found with ID starting with '{run_id}'") + raise typer.Exit(1) + elif len(matching_runs) > 1: + console.print(f"[red]Error:[/red] Multiple runs match '{run_id}'. Use full UUID.") + for run in matching_runs: + console.print(f" - {run.id} ({run.test_name})") + raise typer.Exit(1) + + full_run_id = UUID(matching_runs[0].id) + else: + try: + full_run_id = UUID(run_id) + except ValueError: + console.print(f"[red]Error:[/red] Invalid run ID: '{run_id}'") + raise typer.Exit(1) + + # Create report config + report_config = ReportConfig( + company_name=company or config.reporting.company_name, + logo_path=Path(config.reporting.logo_path) if config.reporting.logo_path else None, + include_charts=config.reporting.include_charts, + chart_dpi=config.reporting.chart_dpi, + ) + + # Create generator + generator = ReportGenerator( + repository=repo, + config=report_config, + reports_dir=Path(config.data.reports_dir), + ) + + # Generate report + console.print(f"[cyan]Generating report for run {str(full_run_id)[:8]}...[/cyan]") + + output_path = Path(output) if output else None + pdf_path = generator.generate(full_run_id, output_path) + + console.print(f"[green]Report saved to:[/green] {pdf_path}") + + except typer.Exit: + raise + except Exception as e: + console.print(f"[red]Error generating report:[/red] {e}") + raise typer.Exit(1) + finally: + repo.close() + + @app.command(name="query") def query_cmd( instrument: Annotated[