From c5dba8c89bba35d7a77e087b180b2a3d7b94280a Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Thu, 29 Oct 2020 21:34:15 -0700 Subject: [PATCH] Added --has-comment/--has-likes to CLI, issue #240 --- README.md | 5 ++ osxphotos/__main__.py | 41 ++++++++++++++++- osxphotos/_version.py | 2 +- tests/test_cli.py | 103 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 134 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 98153a25..ac6f5e3c 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,10 @@ Options: 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601). + --has-comment Search for photos that have comments. + --no-comment Search for photos with no comments. + --has-likes Search for photos that have likes. + --no-likes Search for photos with no likes. --deleted Include photos from the 'Recently Deleted' folder. --deleted-only Include only photos from the 'Recently @@ -542,6 +546,7 @@ Substitution Description (Photos 5 only) {label_normalized} All lower case version of 'label' (Photos 5 only) {comment} Comment(s) on shared Photos; format is 'Person name: + comment text' (Photos 5 only) ``` Example: export all photos to ~/Desktop/export group in folders by date created diff --git a/osxphotos/__main__.py b/osxphotos/__main__.py index bb69a5ad..63da0190 100644 --- a/osxphotos/__main__.py +++ b/osxphotos/__main__.py @@ -489,6 +489,10 @@ def query_options(f): help="Search by end item date, e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601).", type=DateTimeISO8601(), ), + o("--has-comment", is_flag=True, help="Search for photos that have comments."), + o("--no-comment", is_flag=True, help="Search for photos with no comments."), + o("--has-likes", is_flag=True, help="Search for photos that have likes."), + o("--no-likes", is_flag=True, help="Search for photos with no likes."), ] for o in options[::-1]: f = o(f) @@ -529,7 +533,7 @@ def debug_dump(ctx, cli_obj, db, photos_library, dump, uuid, verbose_): global VERBOSE VERBOSE = bool(verbose_) - + db = get_photos_db(*photos_library, db, cli_obj.db) if db is None: click.echo(cli.commands["debug-dump"].get_help(ctx), err=True) @@ -982,6 +986,10 @@ def query( label, deleted, deleted_only, + has_comment, + no_comment, + has_likes, + no_likes, ): """ Query the Photos database using 1 or more search options; if more than one option is provided, they are treated as "AND" @@ -1026,6 +1034,8 @@ def query( (any(place), no_place), (deleted, deleted_only), (shared, not_shared), + (has_comment, no_comment), + (has_likes, no_likes), ] # print help if no non-exclusive term or a double exclusive term is given if any(all(bb) for bb in exclusive) or not any( @@ -1112,6 +1122,10 @@ def query( label=label, deleted=deleted, deleted_only=deleted_only, + has_comment=has_comment, + no_comment=no_comment, + has_likes=has_likes, + no_likes=no_likes, ) # below needed for to make CliRunner work for testing @@ -1395,6 +1409,10 @@ def export( edited_suffix, place, no_place, + has_comment, + no_comment, + has_likes, + no_likes, no_extended_attributes, label, deleted, @@ -1442,6 +1460,8 @@ def export( (skip_edited, skip_original_if_edited), (export_as_hardlink, convert_to_jpeg), (shared, not_shared), + (has_comment, no_comment), + (has_likes, no_likes), ] if any(all(bb) for bb in exclusive): click.echo("Incompatible export options", err=True) @@ -1580,6 +1600,10 @@ def export( label=label, deleted=deleted, deleted_only=deleted_only, + has_comment=has_comment, + no_comment=no_comment, + has_likes=has_likes, + no_likes=no_likes, ) if photos: @@ -1899,6 +1923,10 @@ def _query( label=None, deleted=False, deleted_only=False, + has_comment=False, + no_comment=False, + has_likes=False, + no_likes=False, ): """ run a query against PhotosDB to extract the photos based on user supply criteria used by query and export commands @@ -2118,6 +2146,17 @@ def _query( if has_raw: photos = [p for p in photos if p.has_raw] + if has_comment: + photos = [p for p in photos if p.comments] + elif no_comment: + photos = [p for p in photos if not p.comments] + + if has_likes: + photos = [p for p in photos if p.likes] + elif no_likes: + photos = [p for p in photos if not p.likes] + + return photos diff --git a/osxphotos/_version.py b/osxphotos/_version.py index b478f1c2..1f75773a 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,4 +1,4 @@ """ version info """ -__version__ = "0.36.0" +__version__ = "0.36.1" diff --git a/tests/test_cli.py b/tests/test_cli.py index 59030374..176b9445 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -11,6 +11,7 @@ from osxphotos.exiftool import get_exiftool_path CLI_PHOTOS_DB = "tests/Test-10.15.1.photoslibrary" LIVE_PHOTOS_DB = "tests/Test-Cloud-10.15.1.photoslibrary" RAW_PHOTOS_DB = "tests/Test-RAW-10.15.1.photoslibrary" +COMMENTS_PHOTOS_DB = "tests/Test-Cloud-10.15.6.photoslibrary" PLACES_PHOTOS_DB = "tests/Test-Places-Catalina-10_15_1.photoslibrary" PLACES_PHOTOS_DB_13 = "tests/Test-Places-High-Sierra-10.13.6.photoslibrary" PHOTOS_DB_15_4 = "tests/Test-10.15.4.photoslibrary" @@ -419,6 +420,22 @@ CLI_EXPORT_UUID_FROM_FILE_FILENAMES = [ "wedding_edited.jpeg", ] +UUID_HAS_COMMENTS = [ + "4E4944A0-3E5C-4028-9600-A8709F2FA1DB", + "4AD7C8EF-2991-4519-9D3A-7F44A6F031BE", + "7572C53E-1D6A-410C-A2B1-18CCA3B5AD9F", +] +UUID_NO_COMMENTS = ["4F835581-5AB9-4DEC-9971-3E64A0894B04"] +UUID_HAS_LIKES = [ + "C008048F-8767-4992-85B8-13E798F6DC3C", + "65BADBD7-A50C-4956-96BA-1BB61155DA17", + "4AD7C8EF-2991-4519-9D3A-7F44A6F031BE", +] +UUID_NO_LIKES = [ + "45099D34-A414-464F-94A2-60D6823679C8", + "1C1C8F1F-826B-4A24-B1CB-56628946A834", +] + @pytest.fixture(autouse=True) def reset_globals(): @@ -599,36 +616,92 @@ def test_query_uuid_from_file_1(): assert sorted(UUID_EXPECTED_FROM_FILE) == sorted(uuid_got) -def test_query_uuid_from_file_2(): - """ Test query with --uuid-from-file and --uuid """ +def test_query_has_comment(): + """ Test query with --has-comment """ import json import os import os.path - import osxphotos from osxphotos.__main__ import query runner = CliRunner() cwd = os.getcwd() result = runner.invoke( query, - [ - "--json", - "--db", - os.path.join(cwd, PHOTOS_DB_15_5), - "--uuid-from-file", - UUID_FILE, - "--uuid", - UUID_NOT_FROM_FILE, - ], + ["--json", "--db", os.path.join(cwd, COMMENTS_PHOTOS_DB), "--has-comment"], ) assert result.exit_code == 0 # build list of uuids we got from the output JSON json_got = json.loads(result.output) uuid_got = [photo["uuid"] for photo in json_got] - uuid_expected = UUID_EXPECTED_FROM_FILE.copy() - uuid_expected.append(UUID_NOT_FROM_FILE) - assert sorted(uuid_expected) == sorted(uuid_got) + assert sorted(uuid_got) == sorted(UUID_HAS_COMMENTS) + + +def test_query_no_comment(): + """ Test query with --no-comment """ + import json + import os + import os.path + from osxphotos.__main__ import query + + runner = CliRunner() + cwd = os.getcwd() + result = runner.invoke( + query, ["--json", "--db", os.path.join(cwd, COMMENTS_PHOTOS_DB), "--no-comment"] + ) + assert result.exit_code == 0 + + # build list of uuids we got from the output JSON + json_got = json.loads(result.output) + uuid_got = [photo["uuid"] for photo in json_got] + for uuid in UUID_NO_COMMENTS: + assert uuid in uuid_got + for uuid in uuid_got: + assert uuid not in UUID_HAS_COMMENTS + + +def test_query_has_likes(): + """ Test query with --has-likes""" + import json + import os + import os.path + from osxphotos.__main__ import query + + runner = CliRunner() + cwd = os.getcwd() + result = runner.invoke( + query, + ["--json", "--db", os.path.join(cwd, COMMENTS_PHOTOS_DB), "--has-likes"], + ) + assert result.exit_code == 0 + + # build list of uuids we got from the output JSON + json_got = json.loads(result.output) + uuid_got = [photo["uuid"] for photo in json_got] + assert sorted(uuid_got) == sorted(UUID_HAS_LIKES) + + +def test_query_no_likes(): + """ Test query with --no-likes""" + import json + import os + import os.path + from osxphotos.__main__ import query + + runner = CliRunner() + cwd = os.getcwd() + result = runner.invoke( + query, ["--json", "--db", os.path.join(cwd, COMMENTS_PHOTOS_DB), "--no-likes"] + ) + assert result.exit_code == 0 + + # build list of uuids we got from the output JSON + json_got = json.loads(result.output) + uuid_got = [photo["uuid"] for photo in json_got] + for uuid in UUID_NO_LIKES: + assert uuid in uuid_got + for uuid in uuid_got: + assert uuid not in UUID_HAS_LIKES def test_export():