Fixed from_date and to_date to be timezone aware, closes #193
This commit is contained in:
@@ -77,6 +77,20 @@ def get_photos_db(*db_options):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class DateTimeISO8601(click.ParamType):
|
||||||
|
|
||||||
|
name = "DATETIME"
|
||||||
|
|
||||||
|
def convert(self, value, param, ctx):
|
||||||
|
try:
|
||||||
|
return datetime.datetime.fromisoformat(value)
|
||||||
|
except:
|
||||||
|
self.fail(
|
||||||
|
f"Invalid value for --{param.name}: invalid datetime format {value}. "
|
||||||
|
"Valid format: YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]]"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Click CLI object & context settings
|
# Click CLI object & context settings
|
||||||
class CLI_Obj:
|
class CLI_Obj:
|
||||||
def __init__(self, db=None, json=False, debug=False):
|
def __init__(self, db=None, json=False, debug=False):
|
||||||
@@ -305,7 +319,7 @@ def query_options(f):
|
|||||||
multiple=False,
|
multiple=False,
|
||||||
help="Search for photos with UUID(s) loaded from FILE. "
|
help="Search for photos with UUID(s) loaded from FILE. "
|
||||||
"Format is a single UUID per line. Lines preceeded with # are ignored.",
|
"Format is a single UUID per line. Lines preceeded with # are ignored.",
|
||||||
type=click.Path(exists=True)
|
type=click.Path(exists=True),
|
||||||
),
|
),
|
||||||
o(
|
o(
|
||||||
"--title",
|
"--title",
|
||||||
@@ -454,13 +468,13 @@ def query_options(f):
|
|||||||
),
|
),
|
||||||
o(
|
o(
|
||||||
"--from-date",
|
"--from-date",
|
||||||
help="Search by start item date, e.g. 2000-01-12T12:00:00 or 2000-12-31 (ISO 8601 w/o TZ).",
|
help="Search by start item date, e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601).",
|
||||||
type=click.DateTime(),
|
type=DateTimeISO8601(),
|
||||||
),
|
),
|
||||||
o(
|
o(
|
||||||
"--to-date",
|
"--to-date",
|
||||||
help="Search by end item date, e.g. 2000-01-12T12:00:00 or 2000-12-31 (ISO 8601 w/o TZ).",
|
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=click.DateTime(),
|
type=DateTimeISO8601(),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
for o in options[::-1]:
|
for o in options[::-1]:
|
||||||
@@ -1011,7 +1025,7 @@ def query(
|
|||||||
|
|
||||||
# load UUIDs if necessary and append to any uuids passed with --uuid
|
# load UUIDs if necessary and append to any uuids passed with --uuid
|
||||||
if uuid_from_file:
|
if uuid_from_file:
|
||||||
uuid_list = list(uuid) # Click option is a tuple
|
uuid_list = list(uuid) # Click option is a tuple
|
||||||
uuid_list.extend(load_uuid_from_file(uuid_from_file))
|
uuid_list.extend(load_uuid_from_file(uuid_from_file))
|
||||||
uuid = tuple(uuid_list)
|
uuid = tuple(uuid_list)
|
||||||
|
|
||||||
@@ -1401,7 +1415,7 @@ def export(
|
|||||||
|
|
||||||
# load UUIDs if necessary and append to any uuids passed with --uuid
|
# load UUIDs if necessary and append to any uuids passed with --uuid
|
||||||
if uuid_from_file:
|
if uuid_from_file:
|
||||||
uuid_list = list(uuid) # Click option is a tuple
|
uuid_list = list(uuid) # Click option is a tuple
|
||||||
uuid_list.extend(load_uuid_from_file(uuid_from_file))
|
uuid_list.extend(load_uuid_from_file(uuid_from_file))
|
||||||
uuid = tuple(uuid_list)
|
uuid = tuple(uuid_list)
|
||||||
|
|
||||||
@@ -2363,6 +2377,7 @@ def find_files_in_branch(pathname, filename):
|
|||||||
|
|
||||||
return files
|
return files
|
||||||
|
|
||||||
|
|
||||||
def load_uuid_from_file(filename):
|
def load_uuid_from_file(filename):
|
||||||
""" Load UUIDs from file. Does not validate UUIDs.
|
""" Load UUIDs from file. Does not validate UUIDs.
|
||||||
Format is 1 UUID per line, any line beginning with # is ignored.
|
Format is 1 UUID per line, any line beginning with # is ignored.
|
||||||
@@ -2377,7 +2392,7 @@ def load_uuid_from_file(filename):
|
|||||||
Raises:
|
Raises:
|
||||||
FileNotFoundError if file does not exist
|
FileNotFoundError if file does not exist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not pathlib.Path(filename).is_file():
|
if not pathlib.Path(filename).is_file():
|
||||||
raise FileNotFoundError(f"Could not find file {filename}")
|
raise FileNotFoundError(f"Could not find file {filename}")
|
||||||
|
|
||||||
@@ -2389,5 +2404,6 @@ def load_uuid_from_file(filename):
|
|||||||
uuid.append(line)
|
uuid.append(line)
|
||||||
return uuid
|
return uuid
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
cli() # pylint: disable=no-value-for-parameter
|
cli() # pylint: disable=no-value-for-parameter
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.31.1"
|
__version__ = "0.31.2"
|
||||||
|
|||||||
57
osxphotos/datetime_utils.py
Normal file
57
osxphotos/datetime_utils.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
""" datetime utilities """
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
def get_local_tz():
|
||||||
|
""" return local timezone as datetime.timezone tzinfo """
|
||||||
|
local_tz = (
|
||||||
|
datetime.datetime.now(datetime.timezone(datetime.timedelta(0)))
|
||||||
|
.astimezone()
|
||||||
|
.tzinfo
|
||||||
|
)
|
||||||
|
return local_tz
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_remove_tz(dt):
|
||||||
|
""" remove timezone from a datetime.datetime object
|
||||||
|
dt: datetime.datetime object with tzinfo
|
||||||
|
returns: dt without any timezone info (naive datetime object) """
|
||||||
|
|
||||||
|
if type(dt) != datetime.datetime:
|
||||||
|
raise TypeError(f"dt must be type datetime.datetime, not {type(dt)}")
|
||||||
|
|
||||||
|
dt_new = dt.replace(tzinfo=None)
|
||||||
|
return dt_new
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_has_tz(dt):
|
||||||
|
""" return True if datetime dt has tzinfo else False
|
||||||
|
dt: datetime.datetime
|
||||||
|
returns True if dt is timezone aware, else False """
|
||||||
|
|
||||||
|
if type(dt) != datetime.datetime:
|
||||||
|
raise TypeError(f"dt must be type datetime.datetime, not {type(dt)}")
|
||||||
|
|
||||||
|
if dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_naive_to_local(dt):
|
||||||
|
""" convert naive (timezone unaware) datetime.datetime
|
||||||
|
to aware timezone in local timezone
|
||||||
|
dt: datetime.datetime without timezone
|
||||||
|
returns: datetime.datetime with local timezone """
|
||||||
|
|
||||||
|
if type(dt) != datetime.datetime:
|
||||||
|
raise TypeError(f"dt must be type datetime.datetime, not {type(dt)}")
|
||||||
|
|
||||||
|
if dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None:
|
||||||
|
# has timezone info
|
||||||
|
raise ValueError(
|
||||||
|
"dt must be naive/timezone unaware: "
|
||||||
|
f"{dt} has tzinfo {dt.tzinfo} and offset {dt.tizinfo.utcoffset(dt)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
dt_local = dt.replace(tzinfo=get_local_tz())
|
||||||
|
return dt_local
|
||||||
@@ -87,12 +87,8 @@ class PhotoInfo:
|
|||||||
@property
|
@property
|
||||||
def date(self):
|
def date(self):
|
||||||
""" image creation date as timezone aware datetime object """
|
""" image creation date as timezone aware datetime object """
|
||||||
imagedate = self._info["imageDate"]
|
return self._info["imageDate"]
|
||||||
seconds = self._info["imageTimeZoneOffsetSeconds"] or 0
|
|
||||||
delta = timedelta(seconds=seconds)
|
|
||||||
tz = timezone(delta)
|
|
||||||
return imagedate.astimezone(tz=tz)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def date_modified(self):
|
def date_modified(self):
|
||||||
""" image modification date as timezone aware datetime object
|
""" image modification date as timezone aware datetime object
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import platform
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta, timezone
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ from .._constants import (
|
|||||||
)
|
)
|
||||||
from .._version import __version__
|
from .._version import __version__
|
||||||
from ..albuminfo import AlbumInfo, FolderInfo
|
from ..albuminfo import AlbumInfo, FolderInfo
|
||||||
|
from ..datetime_utils import datetime_has_tz, datetime_naive_to_local
|
||||||
from ..personinfo import PersonInfo
|
from ..personinfo import PersonInfo
|
||||||
from ..photoinfo import PhotoInfo
|
from ..photoinfo import PhotoInfo
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
@@ -952,15 +953,23 @@ class PhotosDB:
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
self._dbphotos[uuid]["lastmodifieddate"] = None
|
self._dbphotos[uuid]["lastmodifieddate"] = None
|
||||||
|
|
||||||
|
self._dbphotos[uuid]["imageTimeZoneOffsetSeconds"] = row[9]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._dbphotos[uuid]["imageDate"] = datetime.fromtimestamp(row[5] + td)
|
imagedate = datetime.fromtimestamp(row[5] + td)
|
||||||
|
seconds = self._dbphotos[uuid]["imageTimeZoneOffsetSeconds"] or 0
|
||||||
|
delta = timedelta(seconds=seconds)
|
||||||
|
tz = timezone(delta)
|
||||||
|
self._dbphotos[uuid]["imageDate"] = imagedate.astimezone(tz=tz)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self._dbphotos[uuid]["imageDate"] = datetime(1970, 1, 1)
|
# sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date
|
||||||
|
imagedate = datetime(1970, 1, 1)
|
||||||
|
tz = timezone(timedelta(0))
|
||||||
|
self._dbphotos[uuid]["imageDate"] = imagedate.astimezone(tz=tz)
|
||||||
|
|
||||||
self._dbphotos[uuid]["mainRating"] = row[6]
|
self._dbphotos[uuid]["mainRating"] = row[6]
|
||||||
self._dbphotos[uuid]["hasAdjustments"] = row[7]
|
self._dbphotos[uuid]["hasAdjustments"] = row[7]
|
||||||
self._dbphotos[uuid]["hasKeywords"] = row[8]
|
self._dbphotos[uuid]["hasKeywords"] = row[8]
|
||||||
self._dbphotos[uuid]["imageTimeZoneOffsetSeconds"] = row[9]
|
|
||||||
self._dbphotos[uuid]["volumeId"] = row[10]
|
self._dbphotos[uuid]["volumeId"] = row[10]
|
||||||
self._dbphotos[uuid]["imagePath"] = row[11]
|
self._dbphotos[uuid]["imagePath"] = row[11]
|
||||||
self._dbphotos[uuid]["extendedDescription"] = row[12]
|
self._dbphotos[uuid]["extendedDescription"] = row[12]
|
||||||
@@ -1329,7 +1338,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
# process faces
|
# process faces
|
||||||
self._process_faceinfo()
|
self._process_faceinfo()
|
||||||
|
|
||||||
# add faces and keywords to photo data
|
# add faces and keywords to photo data
|
||||||
for uuid in self._dbphotos:
|
for uuid in self._dbphotos:
|
||||||
# keywords
|
# keywords
|
||||||
@@ -1788,12 +1797,20 @@ class PhotosDB:
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
info["lastmodifieddate"] = None
|
info["lastmodifieddate"] = None
|
||||||
|
|
||||||
try:
|
|
||||||
info["imageDate"] = datetime.fromtimestamp(row[5] + td)
|
|
||||||
except ValueError:
|
|
||||||
info["imageDate"] = datetime(1970, 1, 1)
|
|
||||||
|
|
||||||
info["imageTimeZoneOffsetSeconds"] = row[6]
|
info["imageTimeZoneOffsetSeconds"] = row[6]
|
||||||
|
|
||||||
|
try:
|
||||||
|
imagedate = datetime.fromtimestamp(row[5] + td)
|
||||||
|
seconds = info["imageTimeZoneOffsetSeconds"] or 0
|
||||||
|
delta = timedelta(seconds=seconds)
|
||||||
|
tz = timezone(delta)
|
||||||
|
info["imageDate"] = imagedate.astimezone(tz=tz)
|
||||||
|
except ValueError:
|
||||||
|
# sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date
|
||||||
|
imagedate = datetime(1970, 1, 1)
|
||||||
|
tz = timezone(timedelta(0))
|
||||||
|
info["imageDate"] = imagedate.astimezone(tz=tz)
|
||||||
|
|
||||||
info["hidden"] = row[9]
|
info["hidden"] = row[9]
|
||||||
info["favorite"] = row[10]
|
info["favorite"] = row[10]
|
||||||
info["originalFilename"] = row[3]
|
info["originalFilename"] = row[3]
|
||||||
@@ -2135,7 +2152,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
# process face info
|
# process face info
|
||||||
self._process_faceinfo()
|
self._process_faceinfo()
|
||||||
|
|
||||||
# process search info
|
# process search info
|
||||||
self._process_searchinfo()
|
self._process_searchinfo()
|
||||||
|
|
||||||
@@ -2436,23 +2453,29 @@ class PhotosDB:
|
|||||||
to_date=None,
|
to_date=None,
|
||||||
intrash=False,
|
intrash=False,
|
||||||
):
|
):
|
||||||
"""
|
""" Return a list of PhotoInfo objects
|
||||||
Return a list of PhotoInfo objects
|
|
||||||
If called with no args, returns the entire database of photos
|
If called with no args, returns the entire database of photos
|
||||||
If called with args, returns photos matching the args (e.g. keywords, persons, etc.)
|
If called with args, returns photos matching the args (e.g. keywords, persons, etc.)
|
||||||
If more than one arg, returns photos matching all the criteria (e.g. keywords AND persons)
|
If more than one arg, returns photos matching all the criteria (e.g. keywords AND persons)
|
||||||
If more than one keyword, uuid, persons, albums is passed, they are treated as "OR" criteria
|
If more than one keyword, uuid, persons, albums is passed, they are treated as "OR" criteria
|
||||||
e.g. keywords=["wedding","vacation"] returns photos matching either keyword
|
e.g. keywords=["wedding","vacation"] returns photos matching either keyword
|
||||||
keywords: list of keywords to search for
|
from_date and to_date may be either naive or timezone-aware datetime.datetime objects.
|
||||||
uuid: list of UUIDs to search for
|
If naive, timezone will be assumed to be local timezone.
|
||||||
persons: list of persons to search for
|
|
||||||
albums: list of album names to search for
|
Args:
|
||||||
images: if True, returns image files, if False, does not return images; default is True
|
keywords: list of keywords to search for
|
||||||
movies: if True, returns movie files, if False, does not return movies; default is True
|
uuid: list of UUIDs to search for
|
||||||
from_date: return photos with creation date >= from_date (datetime.datetime object, default None)
|
persons: list of persons to search for
|
||||||
to_date: return photos with creation date <= to_date (datetime.datetime object, default None)
|
albums: list of album names to search for
|
||||||
intrash: if True, returns only images in "Recently deleted items" folder,
|
images: if True, returns image files, if False, does not return images; default is True
|
||||||
if False returns only photos that aren't deleted; default is False
|
movies: if True, returns movie files, if False, does not return movies; default is True
|
||||||
|
from_date: return photos with creation date >= from_date (datetime.datetime object, default None)
|
||||||
|
to_date: return photos with creation date <= to_date (datetime.datetime object, default None)
|
||||||
|
intrash: if True, returns only images in "Recently deleted items" folder,
|
||||||
|
if False returns only photos that aren't deleted; default is False
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list of PhotoInfo objects
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# implementation is a bit kludgy but it works
|
# implementation is a bit kludgy but it works
|
||||||
@@ -2528,6 +2551,8 @@ class PhotosDB:
|
|||||||
if from_date or to_date: # sourcery off
|
if from_date or to_date: # sourcery off
|
||||||
dsel = self._dbphotos
|
dsel = self._dbphotos
|
||||||
if from_date:
|
if from_date:
|
||||||
|
if not datetime_has_tz(from_date):
|
||||||
|
from_date = datetime_naive_to_local(from_date)
|
||||||
dsel = {
|
dsel = {
|
||||||
k: v for k, v in dsel.items() if v["imageDate"] >= from_date
|
k: v for k, v in dsel.items() if v["imageDate"] >= from_date
|
||||||
}
|
}
|
||||||
@@ -2535,6 +2560,8 @@ class PhotosDB:
|
|||||||
f"Found %i items with from_date {from_date}" % len(dsel)
|
f"Found %i items with from_date {from_date}" % len(dsel)
|
||||||
)
|
)
|
||||||
if to_date:
|
if to_date:
|
||||||
|
if not datetime_has_tz(to_date):
|
||||||
|
to_date = datetime_naive_to_local(to_date)
|
||||||
dsel = {k: v for k, v in dsel.items() if v["imageDate"] <= 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))
|
logging.debug(f"Found %i items with to_date {to_date}" % len(dsel))
|
||||||
photos_sets.append(set(dsel.keys()))
|
photos_sets.append(set(dsel.keys()))
|
||||||
|
|||||||
@@ -46,13 +46,6 @@ def test_db_version():
|
|||||||
assert photosdb.db_version == "2622"
|
assert photosdb.db_version == "2622"
|
||||||
|
|
||||||
|
|
||||||
def test_os_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(_, major, _) = osxphotos.utils._get_os_version()
|
|
||||||
assert major in osxphotos._constants._TESTED_OS_VERSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons():
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@@ -130,13 +130,6 @@ def test_db_version():
|
|||||||
assert photosdb.db_version == "6000"
|
assert photosdb.db_version == "6000"
|
||||||
|
|
||||||
|
|
||||||
def test_os_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(_, major, _) = osxphotos.utils._get_os_version()
|
|
||||||
assert major in osxphotos._constants._TESTED_OS_VERSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons():
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@@ -138,13 +138,6 @@ def test_db_version():
|
|||||||
assert photosdb.db_version == "6000"
|
assert photosdb.db_version == "6000"
|
||||||
|
|
||||||
|
|
||||||
def test_os_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(_, major, _) = osxphotos.utils._get_os_version()
|
|
||||||
assert major in osxphotos._constants._TESTED_OS_VERSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons():
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@@ -164,13 +164,6 @@ def test_db_version():
|
|||||||
assert photosdb.db_version == "6000"
|
assert photosdb.db_version == "6000"
|
||||||
|
|
||||||
|
|
||||||
def test_os_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(_, major, _) = osxphotos.utils._get_os_version()
|
|
||||||
assert major in osxphotos._constants._TESTED_OS_VERSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons():
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@@ -697,13 +697,18 @@ def test_export_edited_suffix():
|
|||||||
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES_EDITED_SUFFIX)
|
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES_EDITED_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
def test_query_date():
|
def test_query_date_1():
|
||||||
|
""" Test --from-date and --to-date """
|
||||||
import json
|
import json
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
import time
|
||||||
from osxphotos.__main__ import query
|
from osxphotos.__main__ import query
|
||||||
|
|
||||||
|
os.environ['TZ'] = "US/Pacific"
|
||||||
|
time.tzset()
|
||||||
|
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
result = runner.invoke(
|
result = runner.invoke(
|
||||||
@@ -721,6 +726,64 @@ def test_query_date():
|
|||||||
json_got = json.loads(result.output)
|
json_got = json.loads(result.output)
|
||||||
assert len(json_got) == 4
|
assert len(json_got) == 4
|
||||||
|
|
||||||
|
def test_query_date_2():
|
||||||
|
""" Test --from-date and --to-date """
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import time
|
||||||
|
from osxphotos.__main__ import query
|
||||||
|
|
||||||
|
os.environ['TZ'] = "Asia/Jerusalem"
|
||||||
|
time.tzset()
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
result = runner.invoke(
|
||||||
|
query,
|
||||||
|
[
|
||||||
|
"--json",
|
||||||
|
"--db",
|
||||||
|
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||||
|
"--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) == 2
|
||||||
|
|
||||||
|
def test_query_date_timezone():
|
||||||
|
""" Test --from-date, --to-date with ISO 8601 timezone """
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import time
|
||||||
|
from osxphotos.__main__ import query
|
||||||
|
|
||||||
|
os.environ['TZ'] = "US/Pacific"
|
||||||
|
time.tzset()
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
result = runner.invoke(
|
||||||
|
query,
|
||||||
|
[
|
||||||
|
"--json",
|
||||||
|
"--db",
|
||||||
|
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||||
|
"--from-date=2018-09-28T00:00:00-07:00",
|
||||||
|
"--to-date=2018-09-28T23:00:00-07:00",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert len(json_got) == 4
|
||||||
|
|
||||||
|
|
||||||
def test_query_keyword_1():
|
def test_query_keyword_1():
|
||||||
"""Test query --keyword """
|
"""Test query --keyword """
|
||||||
|
|||||||
@@ -29,13 +29,6 @@ def test_db_version():
|
|||||||
assert photosdb.db_version == "4025"
|
assert photosdb.db_version == "4025"
|
||||||
|
|
||||||
|
|
||||||
def test_os_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(_, major, _) = osxphotos.utils._get_os_version()
|
|
||||||
assert major in osxphotos._constants._TESTED_OS_VERSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons():
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@@ -46,13 +46,6 @@ def test_db_version():
|
|||||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
||||||
|
|
||||||
|
|
||||||
def test_os_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(_, major, _) = osxphotos.utils._get_os_version()
|
|
||||||
assert major in osxphotos._constants._TESTED_OS_VERSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons():
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@@ -47,13 +47,6 @@ def test_db_version():
|
|||||||
assert photosdb.db_version == "4016"
|
assert photosdb.db_version == "4016"
|
||||||
|
|
||||||
|
|
||||||
def test_os_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(_, major, _) = osxphotos.utils._get_os_version()
|
|
||||||
assert major in osxphotos._constants._TESTED_OS_VERSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons():
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
from osxphotos._constants import _UNKNOWN_PERSON
|
||||||
|
|
||||||
@@ -82,13 +81,6 @@ def test_db_len():
|
|||||||
assert len(photosdb) == PHOTOS_DB_LEN
|
assert len(photosdb) == PHOTOS_DB_LEN
|
||||||
|
|
||||||
|
|
||||||
def test_os_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(_, major, _) = osxphotos.utils._get_os_version()
|
|
||||||
assert major in osxphotos._constants._TESTED_OS_VERSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons():
|
||||||
import osxphotos
|
import osxphotos
|
||||||
import collections
|
import collections
|
||||||
@@ -272,6 +264,7 @@ def test_not_hidden():
|
|||||||
def test_location_1():
|
def test_location_1():
|
||||||
# test photo with lat/lon info
|
# test photo with lat/lon info
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
import pytest
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
photos = photosdb.photos(uuid=["3Jn73XpSQQCluzRBMWRsMA"])
|
photos = photosdb.photos(uuid=["3Jn73XpSQQCluzRBMWRsMA"])
|
||||||
|
|||||||
Reference in New Issue
Block a user