diff --git a/docs/source/user_guide/cli.rst b/docs/source/user_guide/cli.rst index a8057fa8..09434266 100644 --- a/docs/source/user_guide/cli.rst +++ b/docs/source/user_guide/cli.rst @@ -4,8 +4,7 @@ Command Line Interface ====================== -.. automodule:: easylink.cli - .. click:: easylink.cli:easylink :prog: easylink - :show-nested: + :nested: full + :commands: run, generate-dag \ No newline at end of file diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 268b0cdd..5e56c9d4 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -13,5 +13,5 @@ Here we cover several core conceptual topics related using EasyLink. :maxdepth: 2 :glob: - * + cli */index diff --git a/src/easylink/cli.py b/src/easylink/cli.py index c5f26222..e515bf9c 100644 --- a/src/easylink/cli.py +++ b/src/easylink/cli.py @@ -37,10 +37,10 @@ ), ), click.option( - "--timestamp/--no-timestamp", - default=True, - show_default=True, - help="Save the results in a timestamped sub-directory of --output-dir.", + "--no-timestamp", + is_flag=True, + default=False, + help="Do not save the results in a timestamped sub-directory of ``--output-dir``.", ), ] @@ -66,9 +66,8 @@ def easylink(): show_default=True, type=click.Path(exists=True, dir_okay=False, resolve_path=True), help=( - "Path to the specification yaml defining the computing environment to " - "run the pipeline on. If no value is passed, the pipeline will be run " - "locally." + "Path to the computing environment specification yaml. If no value is passed, " + "the pipeline will be run locally." ), ) @click.option("-v", "--verbose", count=True, help="Increase logging verbosity.", hidden=True) @@ -83,17 +82,22 @@ def run( pipeline_specification: str, input_data: str, output_dir: str | None, - timestamp: bool, + no_timestamp: bool, computing_environment: str | None, verbose: int, with_debugger: bool, ) -> None: - """Run a pipeline from the command line.""" + """Runs a pipeline from the command line. + + In addition to running the pipeline, this command will also generate the + DAG image. If you only want to generate the image without actually running + the pipeline, use the ``easylink generate-dag`` command. + """ configure_logging_to_terminal(verbose) logger.info("Running pipeline") - results_dir = get_results_directory(output_dir, timestamp).as_posix() + results_dir = get_results_directory(output_dir, no_timestamp).as_posix() logger.info(f"Results directory: {results_dir}") - # TODO [MIC-4493]: Add configuration validation + # TODO [MIC-4493]: Add configuration validation`` main = handle_exceptions( func=runner.main, exceptions_logger=logger, with_debugger=with_debugger @@ -114,11 +118,15 @@ def generate_dag( pipeline_specification: str, input_data: str, output_dir: str | None, - timestamp: bool, + no_timestamp: bool, ) -> None: - """Generate an image of the proposed pipeline DAG.""" + """Generates an image of the proposed pipeline DAG. + + This command only generates the DAG image of the pipeline; it does not attempt + to actually run it. To run the pipeline, use the ``easylink run`` command. + """ logger.info("Generating DAG") - results_dir = get_results_directory(output_dir, timestamp).as_posix() + results_dir = get_results_directory(output_dir, no_timestamp).as_posix() logger.info(f"Results directory: {results_dir}") # TODO [MIC-4493]: Add configuration validation runner.main( diff --git a/src/easylink/utilities/data_utils.py b/src/easylink/utilities/data_utils.py index 23de922b..6df3f30b 100644 --- a/src/easylink/utilities/data_utils.py +++ b/src/easylink/utilities/data_utils.py @@ -42,9 +42,9 @@ def copy_configuration_files_to_results_directory( shutil.copy(computing_environment, results_dir) -def get_results_directory(output_dir: str | None, timestamp: bool) -> Path: +def get_results_directory(output_dir: str | None, no_timestamp: bool) -> Path: results_dir = Path("results" if output_dir is None else output_dir).resolve() - if timestamp: + if not no_timestamp: launch_time = _get_timestamp() results_dir = results_dir / launch_time return results_dir diff --git a/tests/unit/test_data_utils.py b/tests/unit/test_data_utils.py index eae9d354..13bed35c 100644 --- a/tests/unit/test_data_utils.py +++ b/tests/unit/test_data_utils.py @@ -22,17 +22,17 @@ def test_create_results_directory(test_dir): @pytest.mark.parametrize( - "output_dir_provided, timestamp", + "output_dir_provided, no_timestamp", [ - (False, False), (False, True), - (True, False), + (False, False), (True, True), + (True, False), ], ) -def test_get_results_directory(test_dir, output_dir_provided, timestamp, mocker): +def test_get_results_directory(test_dir, output_dir_provided, no_timestamp, mocker): """Tests expected behavior. If directory is provided then a "results/" folder - is created at the working directory. If timestamp is True, then a timestamped + is created at the working directory. If no_timestamp is False, then a timestamped directory is created within the results directory. """ if output_dir_provided: @@ -44,12 +44,12 @@ def test_get_results_directory(test_dir, output_dir_provided, timestamp, mocker) mocker.patch( "easylink.utilities.data_utils._get_timestamp", return_value="2024_01_01_00_00_00" ) - results_dir = get_results_directory(output_dir, timestamp) + results_dir = get_results_directory(output_dir, no_timestamp) expected_results_dir = Path(test_dir) if not output_dir_provided: expected_results_dir = expected_results_dir / "results" - if timestamp: + if not no_timestamp: expected_results_dir = expected_results_dir / "2024_01_01_00_00_00" assert expected_results_dir == results_dir