Merge pull request #57 from mwort/from-to-date-query

Add --from-date and --to-date to query and export command
This commit is contained in:
Rhet Turnbull
2020-01-20 07:36:35 -08:00
committed by GitHub
4 changed files with 115 additions and 102 deletions

View File

@@ -173,6 +173,16 @@ def query_options(f):
is_flag=True,
help="Search only for photos/images (default searches both images and movies).",
),
o(
"--from-date",
help="Search by start item date, e.g. 2000-01-12T12:00:00 or 2000-12-31 (ISO 8601 w/o TZ).",
type=click.DateTime(),
),
o(
"--to-date",
help="Search by end item date, e.g. 2000-01-12T12:00:00 or 2000-12-31 (ISO 8601 w/o TZ).",
type=click.DateTime(),
),
]
for o in options[::-1]:
f = o(f)
@@ -475,6 +485,8 @@ def query(
not_cloudasset,
incloud,
not_incloud,
from_date,
to_date,
):
""" Query the Photos database using 1 or more search options;
if more than one option is provided, they are treated as "AND"
@@ -482,80 +494,33 @@ def query(
"""
# if no query terms, show help and return
if not any(
[
keyword,
person,
album,
uuid,
title,
no_title,
description,
no_description,
edited,
external_edit,
favorite,
not_favorite,
hidden,
not_hidden,
missing,
not_missing,
shared,
not_shared,
only_movies,
only_photos,
uti,
burst,
not_burst,
live,
not_live,
cloudasset,
not_cloudasset,
incloud,
not_incloud,
]
):
click.echo(cli.commands["query"].get_help(ctx))
return
elif favorite and not_favorite:
# can't search for both favorite and notfavorite
click.echo(cli.commands["query"].get_help(ctx))
return
elif hidden and not_hidden:
# can't search for both hidden and nothidden
click.echo(cli.commands["query"].get_help(ctx))
return
elif missing and not_missing:
# can't search for both missing and notmissing
click.echo(cli.commands["query"].get_help(ctx))
return
elif title and no_title:
# can't search for both title and no_title
click.echo(cli.commands["query"].get_help(ctx))
return
elif description and no_description:
# can't search for both description and no_description
click.echo(cli.commands["query"].get_help(ctx))
return
elif only_photos and only_movies:
# can't have only photos and only movies
click.echo(cli.commands["query"].get_help(ctx))
return
elif burst and not_burst:
# can't search for both burst and not_burst
click.echo(cli.commands["query"].get_help(ctx))
return
elif live and not_live:
# can't search for both live and not_live
click.echo(cli.commands["query"].get_help(ctx))
return
elif cloudasset and not_cloudasset:
# can't search for both live and not_live
click.echo(cli.commands["query"].get_help(ctx))
return
elif incloud and not_incloud:
# can't search for both live and not_live
click.echo(cli.commands["query"].get_help(ctx))
# sanity check input args
nonexclusive = [
keyword,
person,
album,
uuid,
edited,
external_edit,
uti,
from_date,
to_date,
]
exclusive = [
(favorite, not_favorite),
(hidden, not_hidden),
(missing, not_missing),
(any(title), no_title),
(any(description), no_description),
(only_photos, only_movies),
(burst, not_burst),
(live, not_live),
(cloudasset, not_cloudasset),
(incloud, not_incloud),
]
# print help if no non-exclusive term or a double exclusive term is given
if not any(nonexclusive + [b ^ n for b, n in exclusive]):
click.echo(cli.commands["query"].get_help(ctx), err=True)
return
# actually have something to query
@@ -606,6 +571,8 @@ def query(
not_cloudasset=not_cloudasset,
incloud=incloud,
not_incloud=not_incloud,
from_date=from_date,
to_date=to_date,
)
# below needed for to make CliRunner work for testing
@@ -698,6 +665,8 @@ def export(
not_hidden,
shared,
not_shared,
from_date,
to_date,
verbose,
overwrite,
export_by_date,
@@ -727,33 +696,17 @@ def export(
sys.exit("DEST must be valid path")
# sanity check input args
if favorite and not_favorite:
# can't search for both favorite and notfavorite
click.echo(cli.commands["export"].get_help(ctx))
return
elif hidden and not_hidden:
# can't search for both hidden and nothidden
click.echo(cli.commands["export"].get_help(ctx))
return
elif title and no_title:
# can't search for both title and no_title
click.echo(cli.commands["export"].get_help(ctx))
return
elif description and no_description:
# can't search for both description and no_description
click.echo(cli.commands["export"].get_help(ctx))
return
elif only_photos and only_movies:
# can't have only photos and only movies
click.echo(cli.commands["export"].get_help(ctx))
return
elif burst and not_burst:
# can't search for both burst and not_burst
click.echo(cli.commands["export"].get_help(ctx))
return
elif live and not_live:
# can't search for both live and not_live
click.echo(cli.commands["export"].get_help(ctx))
exclusive = [
(favorite, not_favorite),
(hidden, not_hidden),
(any(title), no_title),
(any(description), no_description),
(only_photos, only_movies),
(burst, not_burst),
(live, not_live),
]
if any([all(bb) for bb in exclusive]):
click.echo(cli.commands["export"].get_help(ctx), err=True)
return
isphoto = ismovie = True # default searches for everything
@@ -803,6 +756,8 @@ def export(
not_cloudasset=False,
incloud=False,
not_incloud=False,
from_date=from_date,
to_date=to_date,
)
if photos:
@@ -978,6 +933,8 @@ def _query(
not_cloudasset=None,
incloud=None,
not_incloud=None,
from_date=None,
to_date=None,
):
""" run a query against PhotosDB to extract the photos based on user supply criteria """
""" used by query and export commands """
@@ -992,6 +949,8 @@ def _query(
uuid=uuid,
images=isphoto,
movies=ismovie,
from_date=from_date,
to_date=to_date,
)
if title:

View File

@@ -1318,6 +1318,8 @@ class PhotosDB:
albums=None,
images=True,
movies=False,
from_date=None,
to_date=None,
):
"""
Return a list of PhotoInfo objects
@@ -1328,7 +1330,7 @@ class PhotosDB:
movies: if True, returns movie files, if False, does not return movies; default is False
"""
photos_sets = [] # list of photo sets to perform intersection of
if not keywords and not uuid and not persons and not albums:
if not any([keywords, uuid, persons, albums, from_date, to_date]):
# return all the photos, filtering for images and movies
# append keys of all photos as a single set to photos_sets
photos_sets.append(set(self._dbphotos.keys()))
@@ -1372,6 +1374,19 @@ class PhotosDB:
photos_sets.append(set(self._dbfaces_person[person]))
else:
logging.debug(f"Could not find person '{person}' in database")
if from_date or to_date:
dsel = self._dbphotos
if from_date:
dsel = {
k: v for k, v in dsel.items() if v["imageDate"] >= from_date
}
logging.debug(
f"Found %i items with from_date {from_date}" % len(dsel)
)
if to_date:
dsel = {k: v for k, v in dsel.items() if v["imageDate"] <= to_date}
logging.debug(f"Found %i items with to_date {to_date}" % len(dsel))
photos_sets.append(set(dsel.keys()))
photoinfo = []
if photos_sets: # found some photos

View File

@@ -786,3 +786,20 @@ def test_photosinfo_repr():
k: str(v).encode("utf-8")
for k, v in photo2.__dict__.items()
}
def test_from_to_date():
import osxphotos
import datetime as dt
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
photos = photosdb.photos(from_date=dt.datetime(2018, 10, 28))
assert len(photos) == 2
photos = photosdb.photos(to_date=dt.datetime(2018, 10, 28))
assert len(photos) == 5
photos = photosdb.photos(from_date=dt.datetime(2018, 9, 28),
to_date=dt.datetime(2018, 9, 29))
assert len(photos) == 4

View File

@@ -103,3 +103,25 @@ def test_export():
)
files = glob.glob("*.jpg")
assert files.sort() == CLI_EXPORT_FILENAMES.sort()
def test_query_date():
import json
import osxphotos
from osxphotos.__main__ import query
runner = CliRunner()
result = runner.invoke(
query,
[
"--json",
"--db",
"./tests/Test-10.15.1.photoslibrary",
"--from-date=2018-09-28",
"--to-date=2018-09-28T23:00:00"
],
)
assert result.exit_code == 0
json_got = json.loads(result.output)
assert len(json_got) == 4