"""Init command - Initialize user configuration.""" import click from .config import ( format_config_display, get_config_path, load_user_config, save_user_config, validate_config, ) def prompt_with_default(text: str, default: str = "") -> str: """Prompt user with optional default value. Args: text: Prompt text default: Default value Returns: User input or default """ if default: result = click.prompt(text, default=default, show_default=True) else: result = click.prompt(text, default="", show_default=False) if result == "": return None return result if result else None def init_show(config_file_path, config_path): if not config_file_path.exists(): click.echo(f"No configuration file found at: {config_file_path}") click.echo("\nRun 'ria init' to create a configuration.") return try: config = load_user_config(config_path) click.echo(f"Current Configuration ({config_file_path}):") click.echo("=" * 60) click.echo() click.echo(format_config_display(config)) click.echo() click.echo("To update: ria init") click.echo("To reset: ria init --reset") except Exception as e: click.echo(f"Error reading configuration: {e}", err=True) click.echo("\nRun 'ria init --reset' to recreate the configuration.") def init_reset(config_file_path, config_path, yes): if not config_file_path.exists(): click.echo(f"No configuration file found at: {config_file_path}") return # Show current config try: config = load_user_config(config_path) click.echo(f"This will delete your configuration file at: {config_file_path}") click.echo() click.echo("Current configuration:") for line in format_config_display(config).split("\n"): click.echo(f" {line}") click.echo() except Exception: click.echo(f"Configuration file exists but may be corrupted: {config_file_path}") click.echo() # Confirm deletion if not yes: if not click.confirm("Are you sure you want to reset?", default=False): click.echo("Reset cancelled.") return # Delete config file try: config_file_path.unlink() click.echo("\n✓ Configuration deleted.") click.echo("\nRun 'ria init' to create a new configuration.") except Exception as e: click.echo(f"Error deleting configuration: {e}", err=True) def build_config(author, organization, project, location, testbed): # Build configuration config = {} if author: config["author"] = author if organization: config["organization"] = organization if project: config["project"] = project if location: config["location"] = location if testbed: config["testbed"] = testbed return config def build_sigmf(license_id, hardware, dataset): # Build SigMF section sigmf = {} if license_id: sigmf["license"] = license_id if hardware: sigmf["hw"] = hardware if dataset: sigmf["dataset"] = dataset return sigmf def save_config(config, config_path, use_interactive, warnings): # Save configuration try: saved_path = save_user_config(config, config_path) click.echo(f"\n✓ Configuration saved to: {saved_path}") if use_interactive: click.echo() click.echo("You can view your config anytime with: ria init --show") click.echo("You can update values by running: ria init") # Show warnings in non-interactive mode elif warnings: click.echo() click.echo("Warnings:") for warning in warnings: click.echo(f" ⚠️ {warning}") # TODO message for core team click.echo() click.echo("NOTE: Automatic config integration is not yet implemented.") click.echo("Config values must currently be applied manually with --metadata flags.") click.echo("(Core team TODO: wire config into capture/convert/transmit commands)") return 0 except Exception as e: click.echo(f"\nError saving configuration: {e}", err=True) return 1 @click.command() @click.option("--author", help="Author name (your name)") @click.option("--organization", help="Organization/institution name") @click.option("--project", help="Project name or identifier") @click.option("--location", help="Physical location (lab name, site, etc.)") @click.option("--testbed", help="Testbed identifier") @click.option("--license", "license_id", help="Data license (SPDX identifier, default: Proprietary)") @click.option("--hw", "hardware", help="Hardware description (e.g., PlutoSDR, USRP B210)") @click.option("--dataset", help="Dataset identifier") @click.option("--show", is_flag=True, help="Display current configuration and exit") @click.option("--reset", is_flag=True, help="Delete existing config") @click.option("--config-path", type=click.Path(), help="Use alternate config file location") @click.option("--interactive/--no-interactive", default=None, help="Force interactive mode on/off") @click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompts") def init( author, organization, project, location, testbed, license_id, hardware, dataset, show, reset, config_path, interactive, yes, ): """Initialize user configuration. Creates a configuration file at ~/.ria/config.yaml with default metadata values that will be used across CLI commands. Examples: \b # Interactive setup ria init \b # Non-interactive setup ria init --author "Jane Doe" --project "RF_Analysis" --location "Lab_A" \b # Show current configuration ria init --show \b # Reset configuration ria init --reset """ config_file_path = get_config_path(config_path) # Handle --show flag if show: init_show(config_file_path, config_path) return # Handle --reset flag if reset: init_reset(config_file_path, config_path, yes) return # Determine if we should use interactive mode # Interactive if: no CLI args provided OR --interactive flag OR config file doesn't exist has_cli_args = any([author, organization, project, location, testbed, hardware, dataset]) if interactive is None: # Auto-detect: interactive if no args provided use_interactive = not has_cli_args else: use_interactive = interactive # Load existing config if it exists existing_config = None if config_file_path.exists(): try: existing_config = load_user_config(config_path) except Exception as e: click.echo(f"Warning: Could not load existing config: {e}", err=True) click.echo("Creating new configuration...\n") # Interactive mode if use_interactive: click.echo() click.echo("Welcome to RIA Toolkit Oss SDR CLI Configuration!") click.echo("=" * 60) click.echo() click.echo(f"This will create a configuration file at: {config_file_path}") click.echo() click.echo("These values will be automatically added to recordings and conversions.") click.echo("You can always change these later by running 'ria init' again.") click.echo() click.echo("Press Enter to skip optional fields.") click.echo() # Required information click.echo("Required Information:") click.echo("-" * 20) # Use existing values as defaults author_default = existing_config.get("author", "") if existing_config else "" org_default = existing_config.get("organization", "") if existing_config else "" proj_default = existing_config.get("project", "") if existing_config else "" loc_default = existing_config.get("location", "") if existing_config else "" test_default = existing_config.get("testbed", "") if existing_config else "" author = click.prompt( "Author name (your name)", default=author_default or "", show_default=bool(author_default) ) organization = prompt_with_default("Organization (optional)", org_default) project = prompt_with_default("Project name (optional)", proj_default) location = prompt_with_default("Location (optional)", loc_default) testbed = prompt_with_default("Testbed name (optional)", test_default) # SigMF metadata click.echo() click.echo("SigMF Metadata (optional):") click.echo("-" * 27) sigmf_defaults = existing_config.get("sigmf", {}) if existing_config else {} license_default = sigmf_defaults.get("license", "Proprietary") hw_default = sigmf_defaults.get("hw", "") dataset_default = sigmf_defaults.get("dataset", "") license_id = click.prompt( "License (e.g., Proprietary, CC-BY-4.0, MIT)", default=license_default, show_default=True ) hardware = prompt_with_default("Hardware description (e.g., PlutoSDR)", hw_default) dataset = prompt_with_default("Dataset name (optional)", dataset_default) # Build configuration config = build_config(author, organization, project, location, testbed) # SigMF section sigmf = build_sigmf(license_id, hardware, dataset) if sigmf: config["sigmf"] = sigmf # Validate configuration warnings = validate_config(config) # Show configuration summary if use_interactive: click.echo() click.echo("Configuration Summary:") click.echo("-" * 22) click.echo(format_config_display(config)) click.echo() # Show warnings if warnings: click.echo("Warnings:") for warning in warnings: click.echo(f" ⚠️ {warning}") click.echo() # Confirm save if not yes: if not click.confirm("Save this configuration?", default=True): click.echo("Configuration not saved.") return # Save configuration return save_config(config, config_path, use_interactive, warnings) if __name__ == "__main__": init()