319 lines
10 KiB
Python
319 lines
10 KiB
Python
"""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 'utils 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: utils init")
|
|
click.echo("To reset: utils init --reset")
|
|
except Exception as e:
|
|
click.echo(f"Error reading configuration: {e}", err=True)
|
|
click.echo("\nRun 'utils 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 'utils 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: utils init --show")
|
|
click.echo("You can update values by running: utils 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 ~/.utils/config.yaml with default metadata
|
|
values that will be used across CLI commands.
|
|
|
|
Examples:
|
|
|
|
\b
|
|
# Interactive setup
|
|
utils init
|
|
|
|
\b
|
|
# Non-interactive setup
|
|
utils init --author "Jane Doe" --project "RF_Analysis" --location "Lab_A"
|
|
|
|
\b
|
|
# Show current configuration
|
|
utils init --show
|
|
|
|
\b
|
|
# Reset configuration
|
|
utils 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 Utils 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 'utils 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()
|