Added --uuid-from-file to CLI

This commit is contained in:
Rhet Turnbull 2020-07-31 19:02:52 -07:00
parent 002fce8e93
commit 840e9937be
6 changed files with 211 additions and 8 deletions

View File

@ -138,6 +138,9 @@ Options:
searches top level folders (e.g. does not
look at subfolders)
--uuid UUID Search for photos with UUID(s).
--uuid-from-file FILE Search for photos with UUID(s) loaded from
FILE. Format is a single UUID per line.
Lines preceeded with # are ignored.
--title TITLE Search for TITLE in title of photo.
--no-title Search for photos with no title.
--description DESC Search for DESC in description of photo.

View File

@ -298,6 +298,15 @@ def query_options(f):
multiple=True,
help="Search for photos with UUID(s).",
),
o(
"--uuid-from-file",
metavar="FILE",
default=None,
multiple=False,
help="Search for photos with UUID(s) loaded from FILE. "
"Format is a single UUID per line. Lines preceeded with # are ignored.",
type=click.Path(exists=True)
),
o(
"--title",
metavar="TITLE",
@ -891,6 +900,7 @@ def query(
album,
folder,
uuid,
uuid_from_file,
title,
no_title,
description,
@ -954,6 +964,7 @@ def query(
album,
folder,
uuid,
uuid_from_file,
edited,
external_edit,
uti,
@ -998,6 +1009,12 @@ def query(
if only_photos:
ismovie = False
# load UUIDs if necessary and append to any uuids passed with --uuid
if uuid_from_file:
uuid_list = list(uuid) # Click option is a tuple
uuid_list.extend(load_uuid_from_file(uuid_from_file))
uuid = tuple(uuid_list)
# below needed for to make CliRunner work for testing
cli_db = cli_obj.db if cli_obj is not None else None
db = get_photos_db(*photos_library, db, cli_db)
@ -1247,6 +1264,7 @@ def export(
album,
folder,
uuid,
uuid_from_file,
title,
no_title,
description,
@ -1329,7 +1347,7 @@ def export(
VERBOSE = True if verbose_ else False
if not os.path.isdir(dest):
sys.exit("DEST must be valid path")
sys.exit(f"DEST {dest} must be valid path")
# sanity check input args
exclusive = [
@ -1381,6 +1399,12 @@ def export(
if only_photos:
ismovie = False
# load UUIDs if necessary and append to any uuids passed with --uuid
if uuid_from_file:
uuid_list = list(uuid) # Click option is a tuple
uuid_list.extend(load_uuid_from_file(uuid_from_file))
uuid = tuple(uuid_list)
# below needed for to make CliRunner work for testing
cli_db = cli_obj.db if cli_obj is not None else None
db = get_photos_db(*photos_library, db, cli_db)
@ -2091,6 +2115,12 @@ def export_photo(
)
return ExportResults([], [], [], [], [])
results_exported = []
results_new = []
results_updated = []
results_skipped = []
results_exif_updated = []
filenames = get_filenames_from_template(photo, filename_template, original_name)
for filename in filenames:
verbose(f"Exporting {photo.filename} as {filename}")
@ -2113,11 +2143,6 @@ def export_photo(
)
# export the photo to each path in dest_paths
results_exported = []
results_new = []
results_updated = []
results_skipped = []
results_exif_updated = []
for dest_path in dest_paths:
export_results = photo.export2(
dest_path,
@ -2338,6 +2363,31 @@ def find_files_in_branch(pathname, filename):
return files
def load_uuid_from_file(filename):
""" Load UUIDs from file. Does not validate UUIDs.
Format is 1 UUID per line, any line beginning with # is ignored.
Whitespace is stripped.
Arguments:
filename: file name of the file containing UUIDs
Returns:
list of UUIDs or empty list of no UUIDs in file
Raises:
FileNotFoundError if file does not exist
"""
if not pathlib.Path(filename).is_file():
raise FileNotFoundError(f"Could not find file {filename}")
uuid = []
with open(filename, "r") as uuid_file:
for line in uuid_file:
line = line.strip()
if len(line) and line[0] != "#":
uuid.append(line)
return uuid
if __name__ == "__main__":
cli() # pylint: disable=no-value-for-parameter

View File

@ -1,3 +1,3 @@
""" version info """
__version__ = "0.31.0"
__version__ = "0.31.1"

View File

@ -1,3 +1,5 @@
""" Test the command line interface (CLI) """
import os
import pytest
@ -14,6 +16,8 @@ PHOTOS_DB_15_4 = "tests/Test-10.15.4.photoslibrary"
PHOTOS_DB_15_5 = "tests/Test-10.15.5.photoslibrary"
PHOTOS_DB_14_6 = "tests/Test-10.14.6.photoslibrary"
UUID_FILE = "tests/uuid_from_file.txt"
CLI_OUTPUT_NO_SUBCOMMAND = [
"Options:",
"--db <Photos database path> Specify Photos database path. Path to Photos",
@ -295,6 +299,23 @@ ALBUMS_JSON = {
PERSONS_JSON = {"persons": {"Katie": 3, "Suzy": 2, "_UNKNOWN_": 1, "Maria": 2}}
UUID_EXPECTED_FROM_FILE = [
"E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
"6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
"A92D9C26-3A50-4197-9388-CB5F7DB9FA91",
]
UUID_NOT_FROM_FILE = "D79B8D77-BFFC-460B-9312-034F2877D35B"
CLI_EXPORT_UUID_FROM_FILE_FILENAMES = [
"IMG_1994.JPG",
"IMG_1994.cr2",
"Tulips.jpg",
"Tulips_edited.jpeg",
"wedding.jpg",
"wedding_edited.jpeg",
]
# determine if exiftool installed so exiftool tests can be skipped
try:
exiftool = get_exiftool_path()
@ -390,6 +411,72 @@ def test_query_uuid():
assert json_expected[key_] in json_got[key_]
def test_query_uuid_from_file_1():
""" Test query with --uuid-from-file """
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,
],
)
assert result.exit_code == 0
# build list of uuids we got from the output JSON
json_got = json.loads(result.output)
uuid_got = []
for photo in json_got:
uuid_got.append(photo["uuid"])
assert sorted(UUID_EXPECTED_FROM_FILE) == sorted(uuid_got)
def test_query_uuid_from_file_2():
""" Test query with --uuid-from-file and --uuid """
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,
],
)
assert result.exit_code == 0
# build list of uuids we got from the output JSON
json_got = json.loads(result.output)
uuid_got = []
for photo in json_got:
uuid_got.append(photo["uuid"])
uuid_expected = UUID_EXPECTED_FROM_FILE.copy()
uuid_expected.append(UUID_NOT_FROM_FILE)
assert sorted(uuid_expected) == sorted(uuid_got)
def test_export():
import glob
import os
@ -407,6 +494,33 @@ def test_export():
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES)
def test_export_uuid_from_file():
""" Test export with --uuid-from-file """
import glob
import os
import os.path
import osxphotos
from osxphotos.__main__ 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_5),
".",
"-V",
"--uuid-from-file",
os.path.join(cwd, UUID_FILE),
],
)
assert result.exit_code == 0
files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_UUID_FROM_FILE_FILENAMES)
def test_export_as_hardlink():
import glob
import os
@ -2468,7 +2582,7 @@ def test_albums():
def test_persons():
"""Test osxphotos albums """
"""Test osxphotos persons """
import json
import osxphotos
import os

29
tests/test_cli_utils.py Normal file
View File

@ -0,0 +1,29 @@
""" Test utility functions in __main__.py """
import pytest
UUID_FILE = "tests/uuid_from_file.txt"
MISSING_UUID_FILE = "tests/uuid_not_found.txt"
UUID_EXPECTED_FROM_FILE = [
"E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
"6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
"A92D9C26-3A50-4197-9388-CB5F7DB9FA91",
]
def test_load_uuid_from_file():
"""Test load_uuid_from_file function """
from osxphotos.__main__ import load_uuid_from_file
uuid_got = load_uuid_from_file(UUID_FILE)
assert uuid_got == UUID_EXPECTED_FROM_FILE
def test_load_uuid_from_file_filenotfound():
"""Test load_uuid_from_file function raises error if file not found"""
from osxphotos.__main__ import load_uuid_from_file
with pytest.raises(FileNotFoundError) as err:
uuid_got = load_uuid_from_file(MISSING_UUID_FILE)
assert "Could not find" in str(err.value)

7
tests/uuid_from_file.txt Normal file
View File

@ -0,0 +1,7 @@
# Test file for load_uuid_from_file
E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51
6191423D-8DB8-4D4C-92BE-9BBBA308AAC4
# D79B8D77-BFFC-460B-9312-034F2877D35B
A92D9C26-3A50-4197-9388-CB5F7DB9FA91