.. _api_cli:

===
CLI
===

.. currentmodule:: medtda.cli

The ``cli`` module provides the command-line interface for MedTDA, including argument parsing, configuration management, and batch processing.

.. contents:: Contents
   :local:
   :depth: 2

Overview
========

This module implements the ``medtda`` command-line tool. It can be used for:

* Single file processing
* Batch processing from CSV
* Configuration file management
* Parallel processing

**Main Functions:**

* ``main()`` - CLI entry point
* ``create_parser()`` - Argument parser
* ``process_single_file()`` - Single file processing
* ``process_batch()`` - Batch processing
* ``load_config()`` - Load configuration from YAML
* ``save_config()`` - Save configuration to YAML

Functions
=========

Entry Point
-----------

main
~~~~

.. autofunction:: medtda.cli.main

Main entry point for the CLI.

**Signature:**

.. code-block:: python

   def main() -> int

**Returns:**

* **exit_code** (*int*) - 0 for success, non-zero for error

**Example:**

::

    # Called automatically when using CLI
    # $ medtda -i image.nii.gz -o output.csv

Argument Parser
---------------

create_parser
~~~~~~~~~~~~~

.. autofunction:: medtda.cli.create_parser

Create the argument parser.

**Signature:**

.. code-block:: python

   def create_parser() -> argparse.ArgumentParser

**Returns:**

* **parser** (*ArgumentParser*) - Configured argument parser

**Example:**

::

    from medtda.cli import create_parser

    parser = create_parser()
    args = parser.parse_args(['--help'])

Processing Functions
--------------------

process_single_file
~~~~~~~~~~~~~~~~~~~

.. autofunction:: medtda.cli.process_single_file

Process a single image file.

**Signature:**

.. code-block:: python

   def process_single_file(args: argparse.Namespace) -> int

**Parameters:**

* **args** (*Namespace*) - Parsed command-line arguments

**Returns:**

* **exit_code** (*int*) - 0 for success, non-zero for error

**Example:**

::

    from medtda.cli import create_parser, process_single_file

    parser = create_parser()
    args = parser.parse_args([
        '-i', 'image.nii.gz',
        '-o', 'output.csv',
        '-v', 'persistence_image'
    ])
    
    exit_code = process_single_file(args)

process_batch
~~~~~~~~~~~~~

.. autofunction:: medtda.cli.process_batch

Process multiple images from a CSV file.

**Signature:**

.. code-block:: python

   def process_batch(args: argparse.Namespace) -> int

**Parameters:**

* **args** (*Namespace*) - Parsed command-line arguments

**Returns:**

* **exit_code** (*int*) - 0 for success, non-zero for error

**Example:**

::

    from medtda.cli import create_parser, process_batch

    parser = create_parser()
    args = parser.parse_args([
        '-i', 'batch_list.csv',
        '-o', 'output_dir',
        '--workers', '4'
    ])
    
    exit_code = process_batch(args)

Configuration Functions
-----------------------

load_config
~~~~~~~~~~~

.. autofunction:: medtda.cli.load_config

Load configuration from a YAML file.

**Signature:**

.. code-block:: python

   def load_config(config_path: str) -> Dict[str, Any]

**Parameters:**

* **config_path** (*str*) - Path to YAML configuration file

**Returns:**

* **config** (*dict*) - Configuration dictionary

**Example:**

::

    from medtda.cli import load_config

    config = load_config('config.yaml')
    print(config['vectorization_method'])

save_config
~~~~~~~~~~~

.. autofunction:: medtda.cli.save_config

Save configuration to a YAML file.

**Signature:**

.. code-block:: python

   def save_config(
       args: argparse.Namespace,
       output_path: Path
   ) -> None

**Parameters:**

* **args** (*Namespace*) - Arguments to save
* **output_path** (*Path*) - Output file path

**Example:**

::

    from medtda.cli import save_config
    from pathlib import Path

    save_config(args, Path('saved_config.yaml'))

merge_config_with_args
~~~~~~~~~~~~~~~~~~~~~~

.. autofunction:: medtda.cli.merge_config_with_args

Merge configuration file with command-line arguments.

**Signature:**

.. code-block:: python

   def merge_config_with_args(
       config: Dict[str, Any],
       args: argparse.Namespace
   ) -> argparse.Namespace

**Parameters:**

* **config** (*dict*) - Configuration dictionary
* **args** (*Namespace*) - Command-line arguments

**Returns:**

* **merged_args** (*Namespace*) - Merged arguments (CLI overrides config)

Validation Functions
--------------------

validate_args
~~~~~~~~~~~~~

.. autofunction:: medtda.cli.validate_args

Validate command-line arguments.

**Signature:**

.. code-block:: python

   def validate_args(args: argparse.Namespace) -> None

**Raises:**

* **ValueError** if arguments are invalid

**Example:**

::

    from medtda.cli import validate_args

    try:
        validate_args(args)
    except ValueError as e:
        print(f"Invalid arguments: {e}")

detect_mode
~~~~~~~~~~~

.. autofunction:: medtda.cli.detect_mode

Detect processing mode (single vs batch) from input path.

**Signature:**

.. code-block:: python

   def detect_mode(input_path: str) -> str

**Parameters:**

* **input_path** (*str*) - Path to input file

**Returns:**

* **mode** (*str*) - ``'single'`` or ``'batch'``

Command-Line Arguments
======================

Input/Output
------------

.. list-table::
   :widths: 30 70
   :header-rows: 1

   * - Argument
     - Description
   * - ``-i, --input``
     - Input image file or batch CSV
   * - ``-o, --output``
     - Output CSV file or directory
   * - ``-m, --mask``
     - Mask file (single mode only)
   * - ``--config``
     - YAML configuration file
   * - ``--save-config``
     - Save configuration to file

Preprocessing
-------------

.. list-table::
   :widths: 30 70
   :header-rows: 1

   * - Argument
     - Description
   * - ``--spacing``
     - Target voxel spacing (e.g., ``1.0 1.0 1.0``)
   * - ``--window``
     - CT windowing (center width)
   * - ``--normalize``
     - Enable normalization
   * - ``--normalize-method``
     - Normalization method (minmax/zscore/robust)
   * - ``--crop-to-roi``
     - Crop to ROI bounding box
   * - ``--roi-padding``
     - ROI padding in pixels
   * - ``--label``
     - Extract specific label from mask

Persistent Homology
-------------------

.. list-table::
   :widths: 30 70
   :header-rows: 1

   * - Argument
     - Description
   * - ``--filtration-type``
     - Filtration type (sublevel/superlevel)
   * - ``--construction``
     - Construction method (T/V)
   * - ``--max-dimension``
     - Maximum homology dimension

Vectorization
-------------

.. list-table::
   :widths: 30 70
   :header-rows: 1

   * - Argument
     - Description
   * - ``-v, --vectorization-method``
     - Vectorization method(s)
   * - ``--return-barcodes``
     - Save raw barcodes

Batch Processing
----------------

.. list-table::
   :widths: 30 70
   :header-rows: 1

   * - Argument
     - Description
   * - ``--workers``
     - Number of parallel workers
   * - ``--continue-on-error``
     - Continue batch on errors

Other
-----

.. list-table::
   :widths: 30 70
   :header-rows: 1

   * - Argument
     - Description
   * - ``--verbose``
     - Verbose output
   * - ``--quiet``
     - Suppress output
   * - ``-h, --help``
     - Show help message

Complete Examples
=================

Example 1: Basic Single File
-----------------------------

::

    from medtda.cli import main
    import sys

    # Process single file
    sys.argv = [
        'medtda',
        '-i', 'image.nii.gz',
        '-o', 'features.csv',
        '-v', 'persistence_image'
    ]
    
    exit_code = main()

Example 2: With Mask and Preprocessing
---------------------------------------

::

    sys.argv = [
        'medtda',
        '-i', 'ct_scan.nii.gz',
        '-m', 'liver_mask.nii.gz',
        '-o', 'liver_features.csv',
        '--spacing', '1.0', '1.0', '1.0',
        '--window', '40', '400',
        '--normalize',
        '--normalize-method', 'robust',
        '-v', 'persistence_image'
    ]
    
    main()

Example 3: Batch Processing
----------------------------

::

    sys.argv = [
        'medtda',
        '-i', 'batch_list.csv',
        '-o', 'output_directory',
        '--workers', '4',
        '--continue-on-error',
        '-v', 'persistence_stats',
        '--verbose'
    ]
    
    main()

Example 4: Using Configuration File
------------------------------------

::

    from medtda.cli import load_config, create_parser, process_single_file

    # Load config
    config = load_config('config.yaml')

    # Create parser and parse args
    parser = create_parser()
    args = parser.parse_args([
        '-i', 'image.nii.gz',
        '-o', 'output.csv'
    ])

    # Merge config with args
    from medtda.cli import merge_config_with_args
    merged_args = merge_config_with_args(config, args)

    # Process
    process_single_file(merged_args)

Example 5: Programmatic Batch Processing
-----------------------------------------

::

    from medtda.cli import create_parser, process_batch

    # Create arguments programmatically
    parser = create_parser()
    args = parser.parse_args([
        '-i', 'batch.csv',
        '-o', 'results',
        '--workers', '8',
        '--spacing', '1.0', '1.0', '1.0',
        '--normalize',
        '-v', 'persistence_image',
        '--continue-on-error'
    ])

    # Process batch
    exit_code = process_batch(args)
    
    if exit_code == 0:
        print("Batch processing completed successfully")
    else:
        print(f"Batch processing failed with code {exit_code}")

Configuration File Format
=========================

YAML Configuration
------------------

Create a ``config.yaml`` file::

    # Input/Output
    input: data/images
    output: results/features.csv

    # Preprocessing
    spacing: [1.0, 1.0, 1.0]
    window: [40, 400]
    normalize: true
    normalize_method: robust
    crop_to_roi: true
    roi_padding: 5

    # Persistent Homology
    filtration_type: sublevel
    construction: T
    max_dimension: 2

    # Vectorization
    vectorization_method: persistence_image
    return_barcodes: false

    # Batch Processing
    workers: 4
    continue_on_error: true

Load and use::

    from medtda.cli import load_config, create_parser, merge_config_with_args

    config = load_config('config.yaml')
    parser = create_parser()
    args = parser.parse_args(['--input', 'override.nii.gz'])
    merged = merge_config_with_args(config, args)

Batch CSV Format
================

For batch processing, create a CSV file::

    image_path,mask_path,case_id
    data/patient001.nii.gz,data/patient001_mask.nii.gz,patient001
    data/patient002.nii.gz,data/patient002_mask.nii.gz,patient002
    data/patient003.nii.gz,data/patient003_mask.nii.gz,patient003

**Columns:**

* ``image_path`` - Required: path to image
* ``mask_path`` - Optional: path to mask
* ``case_id`` - Optional: identifier for output files

Error Handling
==============

Single File Mode
----------------

::

    from medtda.cli import process_single_file

    try:
        exit_code = process_single_file(args)
        if exit_code != 0:
            print("Processing failed")
    except Exception as e:
        print(f"Error: {e}")

Batch Mode
----------

::

    from medtda.cli import process_batch

    # With continue-on-error
    args.continue_on_error = True
    exit_code = process_batch(args)
    
    # Check exit code
    if exit_code == 0:
        print("All files processed successfully")
    else:
        print("Some files failed (check error log)")

Parallel Processing
===================

Control Workers
---------------

::

    # Single worker (sequential)
    args.workers = 1

    # 4 workers (parallel)
    args.workers = 4

    # Auto-detect (number of CPUs)
    args.workers = -1

    exit_code = process_batch(args)

Performance Tips
================

1. **Use parallel processing for batch:**
   
   Set workers to number of CPU cores::
   
       --workers 8

2. **Enable continue-on-error:**
   
   Don't stop on individual failures::
   
       --continue-on-error

3. **Use configuration files:**
   
   Avoid long command lines::
   
       --config config.yaml

4. **Choose efficient vectorization:**
   
   For large batches, use fast methods::
   
       -v persistence_stats  # Fastest
       -v betti_curve        # Fast
       -v persistence_image  # Medium

Internal Functions
==================

The following functions are used internally by the CLI:

_process_single_case
~~~~~~~~~~~~~~~~~~~~

.. autofunction:: medtda.cli._process_single_case

Process a single case (internal helper).

_process_batch_sequential
~~~~~~~~~~~~~~~~~~~~~~~~~

.. autofunction:: medtda.cli._process_batch_sequential

Process batch sequentially (internal helper).

_process_batch_parallel
~~~~~~~~~~~~~~~~~~~~~~~

.. autofunction:: medtda.cli._process_batch_parallel

Process batch in parallel (internal helper).

See Also
========

* :doc:`featureextractor` - FeatureExtractor class
* :doc:`../cli` - CLI user guide
* :doc:`../user_guide/batch_processing` - Batch processing guide
* :doc:`../quickstart` - Quick start examples
