""" Shared utilities for Major Domo CLI modules. All CLI sub-modules import from here for consistent output and state management. """ import json import os import sys from typing import Any, List, Optional from rich.console import Console from rich.table import Table # Import the API client from same directory sys.path.insert(0, os.path.dirname(__file__)) from api_client import MajorDomoAPI console = Console() class State: """Global state for API client and settings""" api: Optional[MajorDomoAPI] = None json_output: bool = False current_season: Optional[int] = None state = State() def output_json(data): """Output data as formatted JSON""" console.print_json(json.dumps(data, indent=2, default=str)) def output_table( title: str, columns: List[str], rows: List[List], show_lines: bool = False ): """Output data as a rich table""" table = Table( title=title, show_header=True, header_style="bold cyan", show_lines=show_lines ) for col in columns: table.add_column(col) for row in rows: table.add_row(*[str(cell) if cell is not None else "" for cell in row]) console.print(table) def handle_error(e: Exception, context: str = ""): """Graceful error handling with helpful messages""" import typer error_str = str(e) if "401" in error_str: console.print("[red]Error:[/red] Unauthorized. Check your API_TOKEN.") elif "404" in error_str: console.print(f"[red]Error:[/red] Not found. {context}") elif "Connection" in error_str or "ConnectionError" in error_str: console.print( "[red]Error:[/red] Cannot connect to API. Check network and --env setting." ) else: console.print(f"[red]Error:[/red] {e}") raise typer.Exit(1) def safe_nested(obj: Any, *keys: str, default: str = "N/A") -> str: """Safely extract a value from nested dicts. Returns default if any key is missing or obj isn't a dict.""" for key in keys: if not isinstance(obj, dict): return default obj = obj.get(key) return obj if obj is not None else default def get_season(season: Optional[int] = None) -> int: """Get season, defaulting to current if not specified. Lazy-fetches current season on first call.""" if season is not None: return season if state.current_season is None: current = state.api.get_current() state.current_season = current["season"] return state.current_season