Added --label to CLI, closes #157
This commit is contained in:
19
README.md
19
README.md
@@ -34,7 +34,7 @@ OSXPhotos provides the ability to interact with and query Apple's Photos.app lib
|
|||||||
|
|
||||||
## Supported operating systems
|
## Supported operating systems
|
||||||
|
|
||||||
Only works on MacOS (aka Mac OS X). Tested on MacOS 10.12.6 / Photos 2.0, 10.13.6 / Photos 3.0, MacOS 10.14.5, 10.14.6 / Photos 4.0, MacOS 10.15.1 & 10.15.4 / Photos 5.0.
|
Only works on MacOS (aka Mac OS X). Tested on MacOS 10.12.6 / Photos 2.0, 10.13.6 / Photos 3.0, MacOS 10.14.5, 10.14.6 / Photos 4.0, MacOS 10.15.1 - 10.15.5 / Photos 5.0.
|
||||||
|
|
||||||
Requires python >= 3.8. You can probably get this to run with Python 3.6 or 3.7 (see notes [below](#Installation-instructions)) but only 3.8+ is officially supported.
|
Requires python >= 3.8. You can probably get this to run with Python 3.6 or 3.7 (see notes [below](#Installation-instructions)) but only 3.8+ is officially supported.
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ You can also install directly from [pypi](https://pypi.org/) but you must use py
|
|||||||
|
|
||||||
This package will install a command line utility called `osxphotos` that allows you to query the Photos database. Alternatively, you can also run the command line utility like this: `python3 -m osxphotos`
|
This package will install a command line utility called `osxphotos` that allows you to query the Photos database. Alternatively, you can also run the command line utility like this: `python3 -m osxphotos`
|
||||||
|
|
||||||
If you only care about the command line tool, I recommend installing with [pipx](https://github.com/pipxproject/pipx)
|
If you only care about the command line tool, you can download an executable of the latest [release](https://github.com/RhetTbull/osxphotos/releases). Alternatively, I recommend installing with [pipx](https://github.com/pipxproject/pipx)
|
||||||
|
|
||||||
After installing pipx:
|
After installing pipx:
|
||||||
`pipx install osxphotos`
|
`pipx install osxphotos`
|
||||||
@@ -90,6 +90,7 @@ Commands:
|
|||||||
help Print help; for help on commands: help <command>.
|
help Print help; for help on commands: help <command>.
|
||||||
info Print out descriptive info of the Photos library database.
|
info Print out descriptive info of the Photos library database.
|
||||||
keywords Print out keywords found in the Photos library.
|
keywords Print out keywords found in the Photos library.
|
||||||
|
labels Print out image classification labels found in the Photos...
|
||||||
list Print list of Photos libraries found on the system.
|
list Print list of Photos libraries found on the system.
|
||||||
persons Print out persons (faces) found in the Photos library.
|
persons Print out persons (faces) found in the Photos library.
|
||||||
places Print out places found in the Photos library.
|
places Print out places found in the Photos library.
|
||||||
@@ -125,13 +126,13 @@ Options:
|
|||||||
-V, --verbose Print verbose output.
|
-V, --verbose Print verbose output.
|
||||||
--keyword KEYWORD Search for photos with keyword KEYWORD. If
|
--keyword KEYWORD Search for photos with keyword KEYWORD. If
|
||||||
more than one keyword, treated as "OR", e.g.
|
more than one keyword, treated as "OR", e.g.
|
||||||
find photos match any keyword
|
find photos matching any keyword
|
||||||
--person PERSON Search for photos with person PERSON. If
|
--person PERSON Search for photos with person PERSON. If
|
||||||
more than one person, treated as "OR", e.g.
|
more than one person, treated as "OR", e.g.
|
||||||
find photos match any person
|
find photos matching any person
|
||||||
--album ALBUM Search for photos in album ALBUM. If more
|
--album ALBUM Search for photos in album ALBUM. If more
|
||||||
than one album, treated as "OR", e.g. find
|
than one album, treated as "OR", e.g. find
|
||||||
photos match any album
|
photos matching any album
|
||||||
--folder FOLDER Search for photos in an album in folder
|
--folder FOLDER Search for photos in an album in folder
|
||||||
FOLDER. If more than one folder, treated as
|
FOLDER. If more than one folder, treated as
|
||||||
"OR", e.g. find photos in any FOLDER. Only
|
"OR", e.g. find photos in any FOLDER. Only
|
||||||
@@ -146,11 +147,15 @@ Options:
|
|||||||
geolocation info
|
geolocation info
|
||||||
--no-place Search for photos with no associated place
|
--no-place Search for photos with no associated place
|
||||||
name info (no reverse geolocation info)
|
name info (no reverse geolocation info)
|
||||||
|
--label LABEL Search for photos with image classification
|
||||||
|
label LABEL (Photos 5 only). If more than
|
||||||
|
one label, treated as "OR", e.g. find photos
|
||||||
|
matching any label
|
||||||
--uti UTI Search for photos whose uniform type
|
--uti UTI Search for photos whose uniform type
|
||||||
identifier (UTI) matches UTI
|
identifier (UTI) matches UTI
|
||||||
-i, --ignore-case Case insensitive search for title,
|
-i, --ignore-case Case insensitive search for title,
|
||||||
description, or place. Does not apply to
|
description, place, keyword, person, or
|
||||||
keyword, person, or album.
|
album.
|
||||||
--edited Search for photos that have been edited.
|
--edited Search for photos that have been edited.
|
||||||
--external-edit Search for photos edited in external editor.
|
--external-edit Search for photos edited in external editor.
|
||||||
--favorite Search for photos marked favorite.
|
--favorite Search for photos marked favorite.
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ def query_options(f):
|
|||||||
default=None,
|
default=None,
|
||||||
multiple=True,
|
multiple=True,
|
||||||
help="Search for photos with keyword KEYWORD. "
|
help="Search for photos with keyword KEYWORD. "
|
||||||
'If more than one keyword, treated as "OR", e.g. find photos match any keyword',
|
'If more than one keyword, treated as "OR", e.g. find photos matching any keyword',
|
||||||
),
|
),
|
||||||
o(
|
o(
|
||||||
"--person",
|
"--person",
|
||||||
@@ -253,7 +253,7 @@ def query_options(f):
|
|||||||
default=None,
|
default=None,
|
||||||
multiple=True,
|
multiple=True,
|
||||||
help="Search for photos with person PERSON. "
|
help="Search for photos with person PERSON. "
|
||||||
'If more than one person, treated as "OR", e.g. find photos match any person',
|
'If more than one person, treated as "OR", e.g. find photos matching any person',
|
||||||
),
|
),
|
||||||
o(
|
o(
|
||||||
"--album",
|
"--album",
|
||||||
@@ -261,7 +261,7 @@ def query_options(f):
|
|||||||
default=None,
|
default=None,
|
||||||
multiple=True,
|
multiple=True,
|
||||||
help="Search for photos in album ALBUM. "
|
help="Search for photos in album ALBUM. "
|
||||||
'If more than one album, treated as "OR", e.g. find photos match any album',
|
'If more than one album, treated as "OR", e.g. find photos matching any album',
|
||||||
),
|
),
|
||||||
o(
|
o(
|
||||||
"--folder",
|
"--folder",
|
||||||
@@ -311,6 +311,13 @@ def query_options(f):
|
|||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Search for photos with no associated place name info (no reverse geolocation info)",
|
help="Search for photos with no associated place name info (no reverse geolocation info)",
|
||||||
),
|
),
|
||||||
|
o(
|
||||||
|
"--label",
|
||||||
|
metavar="LABEL",
|
||||||
|
multiple=True,
|
||||||
|
help="Search for photos with image classification label LABEL (Photos 5 only). "
|
||||||
|
'If more than one label, treated as "OR", e.g. find photos matching any label',
|
||||||
|
),
|
||||||
o(
|
o(
|
||||||
"--uti",
|
"--uti",
|
||||||
metavar="UTI",
|
metavar="UTI",
|
||||||
@@ -527,7 +534,9 @@ def debug_dump(ctx, cli_obj, db, photos_library, dump, uuid):
|
|||||||
def keywords(ctx, cli_obj, db, json_, photos_library):
|
def keywords(ctx, cli_obj, db, json_, photos_library):
|
||||||
""" Print out keywords found in the Photos library. """
|
""" Print out keywords found in the Photos library. """
|
||||||
|
|
||||||
db = get_photos_db(*photos_library, db, cli_obj.db)
|
# 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)
|
||||||
if db is None:
|
if db is None:
|
||||||
click.echo(cli.commands["keywords"].get_help(ctx), err=True)
|
click.echo(cli.commands["keywords"].get_help(ctx), err=True)
|
||||||
click.echo("\n\nLocated the following Photos library databases: ", err=True)
|
click.echo("\n\nLocated the following Photos library databases: ", err=True)
|
||||||
@@ -551,7 +560,9 @@ def keywords(ctx, cli_obj, db, json_, photos_library):
|
|||||||
def albums(ctx, cli_obj, db, json_, photos_library):
|
def albums(ctx, cli_obj, db, json_, photos_library):
|
||||||
""" Print out albums found in the Photos library. """
|
""" Print out albums found in the Photos library. """
|
||||||
|
|
||||||
db = get_photos_db(*photos_library, db, cli_obj.db)
|
# 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)
|
||||||
if db is None:
|
if db is None:
|
||||||
click.echo(cli.commands["albums"].get_help(ctx), err=True)
|
click.echo(cli.commands["albums"].get_help(ctx), err=True)
|
||||||
click.echo("\n\nLocated the following Photos library databases: ", err=True)
|
click.echo("\n\nLocated the following Photos library databases: ", err=True)
|
||||||
@@ -578,7 +589,9 @@ def albums(ctx, cli_obj, db, json_, photos_library):
|
|||||||
def persons(ctx, cli_obj, db, json_, photos_library):
|
def persons(ctx, cli_obj, db, json_, photos_library):
|
||||||
""" Print out persons (faces) found in the Photos library. """
|
""" Print out persons (faces) found in the Photos library. """
|
||||||
|
|
||||||
db = get_photos_db(*photos_library, db, cli_obj.db)
|
# 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)
|
||||||
if db is None:
|
if db is None:
|
||||||
click.echo(cli.commands["persons"].get_help(ctx), err=True)
|
click.echo(cli.commands["persons"].get_help(ctx), err=True)
|
||||||
click.echo("\n\nLocated the following Photos library databases: ", err=True)
|
click.echo("\n\nLocated the following Photos library databases: ", err=True)
|
||||||
@@ -593,6 +606,32 @@ def persons(ctx, cli_obj, db, json_, photos_library):
|
|||||||
click.echo(yaml.dump(persons, sort_keys=False))
|
click.echo(yaml.dump(persons, sort_keys=False))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@DB_OPTION
|
||||||
|
@JSON_OPTION
|
||||||
|
@DB_ARGUMENT
|
||||||
|
@click.pass_obj
|
||||||
|
@click.pass_context
|
||||||
|
def labels(ctx, cli_obj, db, json_, photos_library):
|
||||||
|
""" Print out image classification labels found in the Photos library. """
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
if db is None:
|
||||||
|
click.echo(cli.commands["labels"].get_help(ctx), err=True)
|
||||||
|
click.echo("\n\nLocated the following Photos library databases: ", err=True)
|
||||||
|
_list_libraries()
|
||||||
|
return
|
||||||
|
|
||||||
|
photosdb = osxphotos.PhotosDB(dbfile=db)
|
||||||
|
labels = {"labels": photosdb.labels_as_dict}
|
||||||
|
if json_ or cli_obj.json:
|
||||||
|
click.echo(json.dumps(labels))
|
||||||
|
else:
|
||||||
|
click.echo(yaml.dump(labels, sort_keys=False))
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@DB_OPTION
|
@DB_OPTION
|
||||||
@JSON_OPTION
|
@JSON_OPTION
|
||||||
@@ -861,6 +900,7 @@ def query(
|
|||||||
has_raw,
|
has_raw,
|
||||||
place,
|
place,
|
||||||
no_place,
|
no_place,
|
||||||
|
label,
|
||||||
):
|
):
|
||||||
""" Query the Photos database using 1 or more search options;
|
""" Query the Photos database using 1 or more search options;
|
||||||
if more than one option is provided, they are treated as "AND"
|
if more than one option is provided, they are treated as "AND"
|
||||||
@@ -881,6 +921,7 @@ def query(
|
|||||||
has_raw,
|
has_raw,
|
||||||
from_date,
|
from_date,
|
||||||
to_date,
|
to_date,
|
||||||
|
label,
|
||||||
]
|
]
|
||||||
exclusive = [
|
exclusive = [
|
||||||
(favorite, not_favorite),
|
(favorite, not_favorite),
|
||||||
@@ -976,6 +1017,7 @@ def query(
|
|||||||
has_raw=has_raw,
|
has_raw=has_raw,
|
||||||
place=place,
|
place=place,
|
||||||
no_place=no_place,
|
no_place=no_place,
|
||||||
|
label=label,
|
||||||
)
|
)
|
||||||
|
|
||||||
# below needed for to make CliRunner work for testing
|
# below needed for to make CliRunner work for testing
|
||||||
@@ -1209,6 +1251,7 @@ def export(
|
|||||||
place,
|
place,
|
||||||
no_place,
|
no_place,
|
||||||
no_extended_attributes,
|
no_extended_attributes,
|
||||||
|
label,
|
||||||
):
|
):
|
||||||
""" Export photos from the Photos database.
|
""" Export photos from the Photos database.
|
||||||
Export path DEST is required.
|
Export path DEST is required.
|
||||||
@@ -1348,6 +1391,7 @@ def export(
|
|||||||
has_raw=has_raw,
|
has_raw=has_raw,
|
||||||
place=place,
|
place=place,
|
||||||
no_place=no_place,
|
no_place=no_place,
|
||||||
|
label=label,
|
||||||
)
|
)
|
||||||
|
|
||||||
results_exported = []
|
results_exported = []
|
||||||
@@ -1636,6 +1680,7 @@ def _query(
|
|||||||
has_raw=None,
|
has_raw=None,
|
||||||
place=None,
|
place=None,
|
||||||
no_place=None,
|
no_place=None,
|
||||||
|
label=None,
|
||||||
):
|
):
|
||||||
""" run a query against PhotosDB to extract the photos based on user supply criteria
|
""" run a query against PhotosDB to extract the photos based on user supply criteria
|
||||||
used by query and export commands
|
used by query and export commands
|
||||||
@@ -1648,50 +1693,16 @@ def _query(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if album:
|
if album:
|
||||||
photos_album = []
|
photos = get_photos_by_attribute(photos, "albums", album, ignore_case)
|
||||||
if ignore_case:
|
|
||||||
# case-insensitive
|
|
||||||
for a in album:
|
|
||||||
a = a.lower()
|
|
||||||
photos_album.extend(
|
|
||||||
p for p in photos if a in [album.lower() for album in p.albums]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for a in album:
|
|
||||||
photos_album.extend(p for p in photos if a in p.albums)
|
|
||||||
photos = photos_album
|
|
||||||
|
|
||||||
if keyword:
|
if keyword:
|
||||||
photos_keyword = []
|
photos = get_photos_by_attribute(photos, "keywords", keyword, ignore_case)
|
||||||
if ignore_case:
|
|
||||||
# case-insensitive
|
|
||||||
for k in keyword:
|
|
||||||
k = k.lower()
|
|
||||||
photos_keyword.extend(
|
|
||||||
p
|
|
||||||
for p in photos
|
|
||||||
if k in [keyword.lower() for keyword in p.keywords]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for k in keyword:
|
|
||||||
photos_keyword.extend(p for p in photos if k in p.keywords)
|
|
||||||
photos = photos_keyword
|
|
||||||
|
|
||||||
if person:
|
if person:
|
||||||
photos_person = []
|
photos = get_photos_by_attribute(photos, "persons", person, ignore_case)
|
||||||
if ignore_case:
|
|
||||||
# case-insensitive
|
if label:
|
||||||
for prsn in person:
|
photos = get_photos_by_attribute(photos, "labels", label, ignore_case)
|
||||||
prsn = prsn.lower()
|
|
||||||
photos_person.extend(
|
|
||||||
p
|
|
||||||
for p in photos
|
|
||||||
if prsn in [person_.lower() for person_ in p.persons]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for prsn in person:
|
|
||||||
photos_person.extend(p for p in photos if prsn in p.persons)
|
|
||||||
photos = photos_person
|
|
||||||
|
|
||||||
if folder:
|
if folder:
|
||||||
# search for photos in an album in folder
|
# search for photos in an album in folder
|
||||||
@@ -1867,6 +1878,34 @@ def _query(
|
|||||||
return photos
|
return photos
|
||||||
|
|
||||||
|
|
||||||
|
def get_photos_by_attribute(photos, attribute, values, ignore_case):
|
||||||
|
"""Search for photos based on values being in PhotoInfo.attribute
|
||||||
|
|
||||||
|
Args:
|
||||||
|
photos: a list of PhotoInfo objects
|
||||||
|
attribute: str, name of PhotoInfo attribute to search (e.g. keywords, persons, etc)
|
||||||
|
values: list of values to search in property
|
||||||
|
ignore_case: ignore case when searching
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list of PhotoInfo objects matching search criteria
|
||||||
|
"""
|
||||||
|
photos_search = []
|
||||||
|
if ignore_case:
|
||||||
|
# case-insensitive
|
||||||
|
for x in values:
|
||||||
|
x = x.lower()
|
||||||
|
photos_search.extend(
|
||||||
|
p
|
||||||
|
for p in photos
|
||||||
|
if x in [attr.lower() for attr in getattr(p, attribute)]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for x in values:
|
||||||
|
photos_search.extend(p for p in photos if x in getattr(p, attribute))
|
||||||
|
return photos_search
|
||||||
|
|
||||||
|
|
||||||
def export_photo(
|
def export_photo(
|
||||||
photo=None,
|
photo=None,
|
||||||
dest=None,
|
dest=None,
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.29.17"
|
__version__ = "0.29.18"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ CLI_OUTPUT_NO_SUBCOMMAND = [
|
|||||||
" help Print help; for help on commands: help <command>.",
|
" help Print help; for help on commands: help <command>.",
|
||||||
" info Print out descriptive info of the Photos library database.",
|
" info Print out descriptive info of the Photos library database.",
|
||||||
" keywords Print out keywords found in the Photos library.",
|
" keywords Print out keywords found in the Photos library.",
|
||||||
|
" labels Print out image classification labels found in the Photos",
|
||||||
" list Print list of Photos libraries found on the system.",
|
" list Print list of Photos libraries found on the system.",
|
||||||
" persons Print out persons (faces) found in the Photos library.",
|
" persons Print out persons (faces) found in the Photos library.",
|
||||||
" places Print out places found in the Photos library.",
|
" places Print out places found in the Photos library.",
|
||||||
@@ -210,6 +211,62 @@ CLI_EXIFTOOL = {
|
|||||||
"XMP:Subject": ["Kids", "Katie"],
|
"XMP:Subject": ["Kids", "Katie"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LABELS_JSON = {
|
||||||
|
"labels": {
|
||||||
|
"Plant": 5,
|
||||||
|
"Tree": 2,
|
||||||
|
"Sky": 2,
|
||||||
|
"Outdoor": 2,
|
||||||
|
"Art": 2,
|
||||||
|
"Foliage": 2,
|
||||||
|
"Waterways": 1,
|
||||||
|
"River": 1,
|
||||||
|
"Cloudy": 1,
|
||||||
|
"Land": 1,
|
||||||
|
"Water Body": 1,
|
||||||
|
"Water": 1,
|
||||||
|
"Statue": 1,
|
||||||
|
"Window": 1,
|
||||||
|
"Decorative Plant": 1,
|
||||||
|
"Blue Sky": 1,
|
||||||
|
"Palm Tree": 1,
|
||||||
|
"Flower": 1,
|
||||||
|
"Flower Arrangement": 1,
|
||||||
|
"Bouquet": 1,
|
||||||
|
"Vase": 1,
|
||||||
|
"Container": 1,
|
||||||
|
"Camera": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KEYWORDS_JSON = {
|
||||||
|
"keywords": {
|
||||||
|
"Kids": 4,
|
||||||
|
"wedding": 2,
|
||||||
|
"London 2018": 1,
|
||||||
|
"St. James's Park": 1,
|
||||||
|
"England": 1,
|
||||||
|
"United Kingdom": 1,
|
||||||
|
"UK": 1,
|
||||||
|
"London": 1,
|
||||||
|
"flowers": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ALBUMS_JSON = {
|
||||||
|
"albums": {
|
||||||
|
"Raw": 4,
|
||||||
|
"Pumpkin Farm": 3,
|
||||||
|
"AlbumInFolder": 2,
|
||||||
|
"Test Album": 2,
|
||||||
|
"I have a deleted twin": 1,
|
||||||
|
},
|
||||||
|
"shared albums": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
PERSONS_JSON = {"persons": {"Katie": 3, "Suzy": 2, "_UNKNOWN_": 1, "Maria": 1}}
|
||||||
|
|
||||||
# determine if exiftool installed so exiftool tests can be skipped
|
# determine if exiftool installed so exiftool tests can be skipped
|
||||||
try:
|
try:
|
||||||
exiftool = get_exiftool_path()
|
exiftool = get_exiftool_path()
|
||||||
@@ -224,9 +281,10 @@ def test_osxphotos():
|
|||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = runner.invoke(cli, [])
|
result = runner.invoke(cli, [])
|
||||||
output = result.output
|
output = result.output
|
||||||
|
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
for line in CLI_OUTPUT_NO_SUBCOMMAND:
|
for line in CLI_OUTPUT_NO_SUBCOMMAND:
|
||||||
assert line in output
|
assert line.strip() in output
|
||||||
|
|
||||||
|
|
||||||
def test_osxphotos_help_1():
|
def test_osxphotos_help_1():
|
||||||
@@ -239,7 +297,7 @@ def test_osxphotos_help_1():
|
|||||||
output = result.output
|
output = result.output
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
for line in CLI_OUTPUT_NO_SUBCOMMAND:
|
for line in CLI_OUTPUT_NO_SUBCOMMAND:
|
||||||
assert line in output
|
assert line.strip() in output
|
||||||
|
|
||||||
|
|
||||||
def test_osxphotos_help_2():
|
def test_osxphotos_help_2():
|
||||||
@@ -804,6 +862,97 @@ def test_query_album_4():
|
|||||||
assert len(json_got) == 7
|
assert len(json_got) == 7
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_label_1():
|
||||||
|
"""Test query --label"""
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
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, PHOTOS_DB_15_5), "--label", "Statue"],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert len(json_got) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_label_2():
|
||||||
|
"""Test query --label with lower case label """
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
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, PHOTOS_DB_15_5), "--label", "statue"],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert len(json_got) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_label_3():
|
||||||
|
"""Test query --label with lower case label and --ignore-case"""
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
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, PHOTOS_DB_15_5),
|
||||||
|
"--label",
|
||||||
|
"statue",
|
||||||
|
"--ignore-case",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert len(json_got) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_label_4():
|
||||||
|
"""Test query with more than one --label"""
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
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, PHOTOS_DB_15_5),
|
||||||
|
"--label",
|
||||||
|
"Statue",
|
||||||
|
"--label",
|
||||||
|
"Plant",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert len(json_got) == 6
|
||||||
|
|
||||||
|
|
||||||
def test_export_sidecar():
|
def test_export_sidecar():
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
@@ -1904,3 +2053,79 @@ def test_export_directory_template_1_dry_run():
|
|||||||
for filepath in CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES1:
|
for filepath in CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES1:
|
||||||
assert f"Exported {filepath}" in result.output
|
assert f"Exported {filepath}" in result.output
|
||||||
assert not os.path.isfile(os.path.join(workdir, filepath))
|
assert not os.path.isfile(os.path.join(workdir, filepath))
|
||||||
|
|
||||||
|
|
||||||
|
def test_labels():
|
||||||
|
"""Test osxphotos labels """
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from osxphotos.__main__ import labels
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
result = runner.invoke(
|
||||||
|
labels, ["--db", os.path.join(cwd, PHOTOS_DB_15_5), "--json"]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert json_got == LABELS_JSON
|
||||||
|
|
||||||
|
|
||||||
|
def test_keywords():
|
||||||
|
"""Test osxphotos keywords """
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from osxphotos.__main__ import keywords
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
result = runner.invoke(
|
||||||
|
keywords, ["--db", os.path.join(cwd, PHOTOS_DB_15_5), "--json"]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert json_got == KEYWORDS_JSON
|
||||||
|
|
||||||
|
|
||||||
|
def test_albums():
|
||||||
|
"""Test osxphotos albums """
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from osxphotos.__main__ import albums
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
result = runner.invoke(
|
||||||
|
albums, ["--db", os.path.join(cwd, PHOTOS_DB_15_5), "--json"]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert json_got == ALBUMS_JSON
|
||||||
|
|
||||||
|
|
||||||
|
def test_persons():
|
||||||
|
"""Test osxphotos albums """
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from osxphotos.__main__ import persons
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
result = runner.invoke(
|
||||||
|
persons, ["--db", os.path.join(cwd, PHOTOS_DB_15_5), "--json"]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert json_got == PERSONS_JSON
|
||||||
|
|||||||
Reference in New Issue
Block a user