diff --git a/docs/cli/pipeline-approve.rst b/docs/cli/pipeline-approve.rst index 922b17d..45f3467 100644 --- a/docs/cli/pipeline-approve.rst +++ b/docs/cli/pipeline-approve.rst @@ -16,7 +16,10 @@ Usage toasty pipeline approve [--workdir=WORKDIR] {IMAGE-IDs...} The ``IMAGE-IDs`` argument specifies one or more images by their unique -identifiers. +identifiers. You can specify exact ID’s, or `glob patterns`_ as processed by the +Python ``fnmatch`` module. See examples below. + +.. _glob patterns: https://docs.python.org/3/library/fnmatch.html#module-fnmatch The ``WORKDIR`` argument optionally specifies the location of the pipeline workspace directory. The default is the current directory. @@ -51,6 +54,18 @@ If everything is OK, you can mark the image as approved: toasty pipeline approve noao0201b +You can use `glob patterns`_ to match image names. For instance, + +.. code-block:: shell + + toasty pipeline approve "vla*20" "?vlba" + +will match every processed image whose identifier begins with ``vla`` and ends +with ``20``, as well as those whose names are exactly four letters long and end +with ``vlba``. You generally must make sure to encase glob arguments in +quotation marks, as shown above, to prevent your shell from attempting to +process them before Toasty gets a chance to. + After approval of a batch of images, the next step is to :ref:`cli-pipeline-publish`. diff --git a/docs/cli/pipeline-fetch.rst b/docs/cli/pipeline-fetch.rst index 718c22c..7d8af1a 100644 --- a/docs/cli/pipeline-fetch.rst +++ b/docs/cli/pipeline-fetch.rst @@ -16,7 +16,10 @@ Usage toasty pipeline fetch [--workdir=WORKDIR] {IMAGE-IDs...} The ``IMAGE-IDs`` argument specifies one or more images by their unique -identifiers. +identifiers. You can specify exact ID’s, or `glob patterns`_ as processed by the +Python ``fnmatch`` module. See examples below. + +.. _glob patterns: https://docs.python.org/3/library/fnmatch.html#module-fnmatch The ``WORKDIR`` argument optionally specifies the location of the pipeline workspace directory. The default is the current directory. @@ -34,6 +37,22 @@ Fetch two images: After fetching, the next step is to :ref:`cli-pipeline-process-todos`. +Example +======= + +You can use `glob patterns`_ to match candidate names. For instance, + +.. code-block:: shell + + toasty pipeline fetch "rubin-*" "soar?" + +will match every candidate whose name begins with ``rubin-``, as well as those +whose names are exactly five letters long and start with ``soar``. You generally +must make sure to encase glob arguments in quotation marks, as shown above, to +prevent your shell from attempting to process them before Toasty gets a chance +to. + + Notes ===== diff --git a/toasty/pipeline/cli.py b/toasty/pipeline/cli.py index 2f08bdb..ce6a497 100644 --- a/toasty/pipeline/cli.py +++ b/toasty/pipeline/cli.py @@ -12,6 +12,8 @@ pipeline_impl '''.split() import argparse +from fnmatch import fnmatch +import glob import os.path import sys @@ -19,6 +21,34 @@ from ..cli import die, warn from . import NotActionableError +def evaluate_imageid_args(searchdir, args): + """ + Figure out which image-ID's to process. + """ + + matched_ids = set() + globs_todo = set() + + for arg in args: + if glob.has_magic(arg): + globs_todo.add(arg) + else: + # If an ID is explicitly (non-gobbily) added, always add it to the + # list, without checking if it exists in `searchdir`. We could check + # for it in searchdir now, but we'll have to check later anyway, so + # we don't bother. + matched_ids.add(arg) + + if len(globs_todo): + for filename in os.listdir(searchdir): + for g in globs_todo: + if fnmatch(filename, g): + matched_ids.add(filename) + break + + return sorted(matched_ids) + + # The "approve" subcommand def approve_setup_parser(parser): @@ -31,8 +61,8 @@ def approve_setup_parser(parser): parser.add_argument( 'cand_ids', nargs = '+', - metavar = 'CAND-ID', - help = 'Name(s) of candidate(s) to approve and prepare for processing' + metavar = 'IMAGE-ID', + help = 'Name(s) of image(s) to approve for publication (globs accepted)' ) @@ -51,7 +81,7 @@ def approve_impl(settings): proc_dir = mgr._ensure_dir('processed') app_dir = mgr._ensure_dir('approved') - for cid in settings.cand_ids: + for cid in evaluate_imageid_args(proc_dir, settings.cand_ids): if not os.path.isdir(os.path.join(proc_dir, cid)): die(f'no such processed candidate ID {cid!r}') @@ -90,7 +120,7 @@ def fetch_setup_parser(parser): 'cand_ids', nargs = '+', metavar = 'CAND-ID', - help = 'Name(s) of candidate(s) to fetch and prepare for processing' + help = 'Name(s) of candidate(s) to fetch and prepare for processing (globs accepted)' ) @@ -102,7 +132,7 @@ def fetch_impl(settings): rej_dir = mgr._ensure_dir('rejects') src = mgr.get_image_source() - for cid in settings.cand_ids: + for cid in evaluate_imageid_args(cand_dir, settings.cand_ids): # Funky structure here is to try to ensure that cdata is closed in case # a NotActionable happens, so that we can move the directory on Windows. try: