From b4bc906b6a1c3444c5f5a5d9d908ab8c955c8f7e Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Thu, 8 Apr 2021 22:15:58 -0700 Subject: [PATCH] Added --query-eval, implements #280 --- README.md | 13 +++++++++++++ osxphotos/_version.py | 2 +- osxphotos/cli.py | 30 ++++++++++++++++++++++++++++++ tests/test_cli.py | 31 +++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 59ab55ce..a2abfd8e 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,19 @@ Options: albums. --not-in-album Search for photos that are not in any albums. + --query-eval CRITERIA Evaluate CRITERIA to filter photos. CRITERIA + will be evaluated in context of the following + list comprehension: `photos = [photo for photo + in photos if CRITERIA]` where photo represents + a PhotoInfo object. For example: `--query-eval + photo.favorite` returns all photos that have + been favorited and is equivalent to + --favorite. You may specify more than one + CRITERIA by using --query-eval multiple times. + See https://rhettbull.github.io/osxphotos/ for + additional documentation on the PhotoInfo + class. + --missing Export only photos missing from the Photos library; must be used with --download-missing. diff --git a/osxphotos/_version.py b/osxphotos/_version.py index 30697c7c..772218c9 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,3 +1,3 @@ """ version info """ -__version__ = "0.41.9" +__version__ = "0.41.10" diff --git a/osxphotos/cli.py b/osxphotos/cli.py index 0ed80f26..e4c44277 100644 --- a/osxphotos/cli.py +++ b/osxphotos/cli.py @@ -447,6 +447,19 @@ def query_options(f): is_flag=True, help="Search for photos that are not in any albums.", ), + o( + "--query-eval", + metavar="CRITERIA", + multiple=True, + help="Evaluate CRITERIA to filter photos. " + "CRITERIA will be evaluated in context of the following list comprehension: " + "`photos = [photo for photo in photos if CRITERIA]` " + "where photo represents a PhotoInfo object. " + "For example: `--query-eval photo.favorite` returns all photos that have been " + "favorited and is equivalent to --favorite. " + "You may specify more than one CRITERIA by using --query-eval multiple times. " + "See https://rhettbull.github.io/osxphotos/ for additional documentation on the PhotoInfo class.", + ), ] for o in options[::-1]: f = o(f) @@ -984,6 +997,7 @@ def export( beta, in_album, not_in_album, + query_eval, ): """Export photos from the Photos database. Export path DEST is required. @@ -1131,6 +1145,7 @@ def export( only_new = cfg.only_new in_album = cfg.in_album not_in_album = cfg.not_in_album + query_eval = cfg.query_eval # config file might have changed verbose VERBOSE = bool(verbose) @@ -1433,6 +1448,7 @@ def export( # skip missing bursts if using --download-missing by itself as AppleScript otherwise causes errors missing_bursts=(download_missing and use_photokit) or not download_missing, name=name, + query_eval=query_eval, ) if photos: @@ -1718,6 +1734,7 @@ def query( is_reference, in_album, not_in_album, + query_eval, ): """Query the Photos database using 1 or more search options; if more than one option is provided, they are treated as "AND" @@ -1744,6 +1761,7 @@ def query( to_time, label, is_reference, + query_eval, ] exclusive = [ (favorite, not_favorite), @@ -1866,6 +1884,7 @@ def query( in_album=in_album, not_in_album=not_in_album, name=name, + query_eval=query_eval, ) # below needed for to make CliRunner work for testing @@ -2044,6 +2063,7 @@ def _query( burst_photos=None, missing_bursts=None, name=None, + query_eval=None, ): """Run a query against PhotosDB to extract the photos based on user supply criteria used by query and export commands @@ -2338,6 +2358,16 @@ def _query( ) photos = photo_list + if query_eval: + for q in query_eval: + query_string = f"[photo for photo in photos if {q}]" + try: + photos = eval(query_string) + except Exception as e: + raise click.BadOptionUsage( + "query_eval", f"Invalid query-eval CRITERIA: {e}" + ) + return photos diff --git a/tests/test_cli.py b/tests/test_cli.py index f1f1893e..27dbface 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -5768,3 +5768,34 @@ def test_export_name(): files = glob.glob("*") assert len(files) == 1 +def test_query_eval(): + """ test export --query-eval """ + import glob + from osxphotos.cli import export + + runner = CliRunner() + cwd = os.getcwd() + # pylint: disable=not-context-manager + with runner.isolated_filesystem(): + result = runner.invoke( + export, [os.path.join(cwd, PHOTOS_DB_15_7), ".", "-V", "--query-eval", "'DSC03584' in photo.original_filename"] + ) + assert result.exit_code == 0 + files = glob.glob("*") + assert len(files) == 1 + + +def test_bad_query_eval(): + """ test export --query-eval with bad input """ + import glob + from osxphotos.cli import export + + runner = CliRunner() + cwd = os.getcwd() + # pylint: disable=not-context-manager + with runner.isolated_filesystem(): + result = runner.invoke( + export, [os.path.join(cwd, PHOTOS_DB_15_7), ".", "-V", "--query-eval", "'DSC03584' in photo.originalfilename"] + ) + assert result.exit_code != 0 + assert "Error: Invalid query-eval CRITERIA" in result.output \ No newline at end of file