Cleaned up tests, fixed bug in PhotosDB.query
This commit is contained in:
parent
4b6c35b5f9
commit
0758f84dc4
@ -1497,7 +1497,7 @@ Substitution Description
|
|||||||
{lf} A line feed: '\n', alias for {newline}
|
{lf} A line feed: '\n', alias for {newline}
|
||||||
{cr} A carriage return: '\r'
|
{cr} A carriage return: '\r'
|
||||||
{crlf} a carriage return + line feed: '\r\n'
|
{crlf} a carriage return + line feed: '\r\n'
|
||||||
{osxphotos_version} The osxphotos version, e.g. '0.42.28'
|
{osxphotos_version} The osxphotos version, e.g. '0.42.30'
|
||||||
{osxphotos_cmd_line} The full command line used to run osxphotos
|
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||||
|
|
||||||
The following substitutions may result in multiple values. Thus if specified for
|
The following substitutions may result in multiple values. Thus if specified for
|
||||||
@ -3194,7 +3194,7 @@ The following template field substitutions are availabe for use the templating s
|
|||||||
|{lf}|A line feed: '\n', alias for {newline}|
|
|{lf}|A line feed: '\n', alias for {newline}|
|
||||||
|{cr}|A carriage return: '\r'|
|
|{cr}|A carriage return: '\r'|
|
||||||
|{crlf}|a carriage return + line feed: '\r\n'|
|
|{crlf}|a carriage return + line feed: '\r\n'|
|
||||||
|{osxphotos_version}|The osxphotos version, e.g. '0.42.28'|
|
|{osxphotos_version}|The osxphotos version, e.g. '0.42.30'|
|
||||||
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||||
|{album}|Album(s) photo is contained in|
|
|{album}|Album(s) photo is contained in|
|
||||||
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||||
|
|||||||
@ -80,7 +80,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def filename(self):
|
def filename(self):
|
||||||
""" filename of the picture """
|
"""filename of the picture"""
|
||||||
if (
|
if (
|
||||||
self._db._db_version <= _PHOTOS_4_VERSION
|
self._db._db_version <= _PHOTOS_4_VERSION
|
||||||
and self.has_raw
|
and self.has_raw
|
||||||
@ -108,7 +108,7 @@ 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"""
|
||||||
return self._info["imageDate"]
|
return self._info["imageDate"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -134,12 +134,12 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def tzoffset(self):
|
def tzoffset(self):
|
||||||
""" timezone offset from UTC in seconds """
|
"""timezone offset from UTC in seconds"""
|
||||||
return self._info["imageTimeZoneOffsetSeconds"]
|
return self._info["imageTimeZoneOffsetSeconds"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self):
|
||||||
""" absolute path on disk of the original picture """
|
"""absolute path on disk of the original picture"""
|
||||||
try:
|
try:
|
||||||
return self._path
|
return self._path
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -211,7 +211,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_edited(self):
|
def path_edited(self):
|
||||||
""" absolute path on disk of the edited picture """
|
"""absolute path on disk of the edited picture"""
|
||||||
""" None if photo has not been edited """
|
""" None if photo has not been edited """
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -225,7 +225,7 @@ class PhotoInfo:
|
|||||||
return self._path_edited
|
return self._path_edited
|
||||||
|
|
||||||
def _path_edited_5(self):
|
def _path_edited_5(self):
|
||||||
""" return path_edited for Photos >= 5 """
|
"""return path_edited for Photos >= 5"""
|
||||||
# In Photos 5.0 / Catalina / MacOS 10.15:
|
# In Photos 5.0 / Catalina / MacOS 10.15:
|
||||||
# edited photos appear to always be converted to .jpeg and stored in
|
# edited photos appear to always be converted to .jpeg and stored in
|
||||||
# library_name/resources/renders/X/UUID_1_201_a.jpeg
|
# library_name/resources/renders/X/UUID_1_201_a.jpeg
|
||||||
@ -283,7 +283,7 @@ class PhotoInfo:
|
|||||||
return photopath
|
return photopath
|
||||||
|
|
||||||
def _path_edited_4(self):
|
def _path_edited_4(self):
|
||||||
""" return path_edited for Photos <= 4 """
|
"""return path_edited for Photos <= 4"""
|
||||||
|
|
||||||
if self._db._db_version > _PHOTOS_4_VERSION:
|
if self._db._db_version > _PHOTOS_4_VERSION:
|
||||||
raise RuntimeError("Wrong database format!")
|
raise RuntimeError("Wrong database format!")
|
||||||
@ -343,7 +343,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_raw(self):
|
def path_raw(self):
|
||||||
""" absolute path of associated RAW image or None if there is not one """
|
"""absolute path of associated RAW image or None if there is not one"""
|
||||||
|
|
||||||
# In Photos 5, raw is in same folder as original but with _4.ext
|
# In Photos 5, raw is in same folder as original but with _4.ext
|
||||||
# Unless "Copy Items to the Photos Library" is not checked
|
# Unless "Copy Items to the Photos Library" is not checked
|
||||||
@ -413,17 +413,17 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
""" long / extended description of picture """
|
"""long / extended description of picture"""
|
||||||
return self._info["extendedDescription"]
|
return self._info["extendedDescription"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def persons(self):
|
def persons(self):
|
||||||
""" list of persons in picture """
|
"""list of persons in picture"""
|
||||||
return [self._db._dbpersons_pk[pk]["fullname"] for pk in self._info["persons"]]
|
return [self._db._dbpersons_pk[pk]["fullname"] for pk in self._info["persons"]]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def person_info(self):
|
def person_info(self):
|
||||||
""" list of PersonInfo objects for person in picture """
|
"""list of PersonInfo objects for person in picture"""
|
||||||
try:
|
try:
|
||||||
return self._personinfo
|
return self._personinfo
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -434,7 +434,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def face_info(self):
|
def face_info(self):
|
||||||
""" list of FaceInfo objects for faces in picture """
|
"""list of FaceInfo objects for faces in picture"""
|
||||||
try:
|
try:
|
||||||
return self._faceinfo
|
return self._faceinfo
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -448,7 +448,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def albums(self):
|
def albums(self):
|
||||||
""" list of albums picture is contained in """
|
"""list of albums picture is contained in"""
|
||||||
try:
|
try:
|
||||||
return self._albums
|
return self._albums
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -460,7 +460,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def burst_albums(self):
|
def burst_albums(self):
|
||||||
"""If photo is burst photo, list of albums it is contained in as well as any albums the key photo is contained in, otherwise returns self.albums """
|
"""If photo is burst photo, list of albums it is contained in as well as any albums the key photo is contained in, otherwise returns self.albums"""
|
||||||
try:
|
try:
|
||||||
return self._burst_albums
|
return self._burst_albums
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -473,7 +473,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def album_info(self):
|
def album_info(self):
|
||||||
""" list of AlbumInfo objects representing albums the photo is contained in """
|
"""list of AlbumInfo objects representing albums the photo is contained in"""
|
||||||
try:
|
try:
|
||||||
return self._album_info
|
return self._album_info
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -485,7 +485,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def burst_album_info(self):
|
def burst_album_info(self):
|
||||||
""" If photo is a burst photo, returns list of AlbumInfo objects representing albums the photo is contained in as well as albums the burst key photo is contained in, otherwise returns self.album_info. """
|
"""If photo is a burst photo, returns list of AlbumInfo objects representing albums the photo is contained in as well as albums the burst key photo is contained in, otherwise returns self.album_info."""
|
||||||
try:
|
try:
|
||||||
return self._burst_album_info
|
return self._burst_album_info
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -498,7 +498,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def import_info(self):
|
def import_info(self):
|
||||||
""" ImportInfo object representing import session for the photo or None if no import session """
|
"""ImportInfo object representing import session for the photo or None if no import session"""
|
||||||
try:
|
try:
|
||||||
return self._import_info
|
return self._import_info
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -511,17 +511,17 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def keywords(self):
|
def keywords(self):
|
||||||
""" list of keywords for picture """
|
"""list of keywords for picture"""
|
||||||
return self._info["keywords"]
|
return self._info["keywords"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
""" name / title of picture """
|
"""name / title of picture"""
|
||||||
return self._info["name"]
|
return self._info["name"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def uuid(self):
|
def uuid(self):
|
||||||
""" UUID of picture """
|
"""UUID of picture"""
|
||||||
return self._uuid
|
return self._uuid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -539,12 +539,12 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def hasadjustments(self):
|
def hasadjustments(self):
|
||||||
""" True if picture has adjustments / edits """
|
"""True if picture has adjustments / edits"""
|
||||||
return self._info["hasAdjustments"] == 1
|
return self._info["hasAdjustments"] == 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def adjustments(self):
|
def adjustments(self):
|
||||||
""" Returns AdjustmentsInfo class for adjustment data or None if no adjustments; Photos 5+ only """
|
"""Returns AdjustmentsInfo class for adjustment data or None if no adjustments; Photos 5+ only"""
|
||||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
if self._db._db_version <= _PHOTOS_4_VERSION:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -568,32 +568,32 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def external_edit(self):
|
def external_edit(self):
|
||||||
""" Returns True if picture was edited outside of Photos using external editor """
|
"""Returns True if picture was edited outside of Photos using external editor"""
|
||||||
return self._info["adjustmentFormatID"] == "com.apple.Photos.externalEdit"
|
return self._info["adjustmentFormatID"] == "com.apple.Photos.externalEdit"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def favorite(self):
|
def favorite(self):
|
||||||
""" True if picture is marked as favorite """
|
"""True if picture is marked as favorite"""
|
||||||
return self._info["favorite"] == 1
|
return self._info["favorite"] == 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hidden(self):
|
def hidden(self):
|
||||||
""" True if picture is hidden """
|
"""True if picture is hidden"""
|
||||||
return self._info["hidden"] == 1
|
return self._info["hidden"] == 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def visible(self):
|
def visible(self):
|
||||||
""" True if picture is visble """
|
"""True if picture is visble"""
|
||||||
return self._info["visible"]
|
return self._info["visible"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def intrash(self):
|
def intrash(self):
|
||||||
""" True if picture is in trash ('Recently Deleted' folder)"""
|
"""True if picture is in trash ('Recently Deleted' folder)"""
|
||||||
return self._info["intrash"]
|
return self._info["intrash"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def date_trashed(self):
|
def date_trashed(self):
|
||||||
""" Date asset was placed in the trash or None """
|
"""Date asset was placed in the trash or None"""
|
||||||
# TODO: add add_timezone(dt, offset_seconds) to datetime_utils
|
# TODO: add add_timezone(dt, offset_seconds) to datetime_utils
|
||||||
# also update date_modified
|
# also update date_modified
|
||||||
trasheddate = self._info["trasheddate"]
|
trasheddate = self._info["trasheddate"]
|
||||||
@ -607,7 +607,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def date_added(self):
|
def date_added(self):
|
||||||
""" Date photo was added to the database """
|
"""Date photo was added to the database"""
|
||||||
try:
|
try:
|
||||||
return self._date_added
|
return self._date_added
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -624,7 +624,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def location(self):
|
def location(self):
|
||||||
""" returns (latitude, longitude) as float in degrees or None """
|
"""returns (latitude, longitude) as float in degrees or None"""
|
||||||
return (self._latitude, self._longitude)
|
return (self._latitude, self._longitude)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -720,27 +720,27 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def isreference(self):
|
def isreference(self):
|
||||||
""" Returns True if photo is a reference (not copied to the Photos library), otherwise False """
|
"""Returns True if photo is a reference (not copied to the Photos library), otherwise False"""
|
||||||
return self._info["isreference"]
|
return self._info["isreference"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def burst(self):
|
def burst(self):
|
||||||
""" Returns True if photo is part of a Burst photo set, otherwise False """
|
"""Returns True if photo is part of a Burst photo set, otherwise False"""
|
||||||
return self._info["burst"]
|
return self._info["burst"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def burst_selected(self):
|
def burst_selected(self):
|
||||||
""" Returns True if photo is a burst photo and has been selected from the burst set by the user, otherwise False """
|
"""Returns True if photo is a burst photo and has been selected from the burst set by the user, otherwise False"""
|
||||||
return bool(self._info["burstPickType"] & BURST_SELECTED)
|
return bool(self._info["burstPickType"] & BURST_SELECTED)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def burst_key(self):
|
def burst_key(self):
|
||||||
""" Returns True if photo is a burst photo and is the key image for the burst set (the image that Photos shows on top of the burst stack), otherwise False """
|
"""Returns True if photo is a burst photo and is the key image for the burst set (the image that Photos shows on top of the burst stack), otherwise False"""
|
||||||
return bool(self._info["burstPickType"] & BURST_KEY)
|
return bool(self._info["burstPickType"] & BURST_KEY)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def burst_default_pick(self):
|
def burst_default_pick(self):
|
||||||
""" Returns True if photo is a burst image and is the photo that Photos selected as the default image for the burst set, otherwise False """
|
"""Returns True if photo is a burst image and is the photo that Photos selected as the default image for the burst set, otherwise False"""
|
||||||
return bool(self._info["burstPickType"] & BURST_DEFAULT_PICK)
|
return bool(self._info["burstPickType"] & BURST_DEFAULT_PICK)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -760,7 +760,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def live_photo(self):
|
def live_photo(self):
|
||||||
""" Returns True if photo is a live photo, otherwise False """
|
"""Returns True if photo is a live photo, otherwise False"""
|
||||||
return self._info["live_photo"]
|
return self._info["live_photo"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -821,7 +821,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_derivatives(self):
|
def path_derivatives(self):
|
||||||
""" Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first) """
|
"""Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first)"""
|
||||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
if self._db._db_version <= _PHOTOS_4_VERSION:
|
||||||
return self._path_derivatives_4()
|
return self._path_derivatives_4()
|
||||||
|
|
||||||
@ -838,7 +838,7 @@ class PhotoInfo:
|
|||||||
return [str(filename) for filename in files if filename.suffix != ".THM"]
|
return [str(filename) for filename in files if filename.suffix != ".THM"]
|
||||||
|
|
||||||
def _path_derivatives_4(self):
|
def _path_derivatives_4(self):
|
||||||
""" Return paths to all derivative (preview) files for Photos <= 4"""
|
"""Return paths to all derivative (preview) files for Photos <= 4"""
|
||||||
modelid = self._info["modelID"]
|
modelid = self._info["modelID"]
|
||||||
if modelid is None:
|
if modelid is None:
|
||||||
return []
|
return []
|
||||||
@ -875,42 +875,42 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def panorama(self):
|
def panorama(self):
|
||||||
""" Returns True if photo is a panorama, otherwise False """
|
"""Returns True if photo is a panorama, otherwise False"""
|
||||||
return self._info["panorama"]
|
return self._info["panorama"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def slow_mo(self):
|
def slow_mo(self):
|
||||||
""" Returns True if photo is a slow motion video, otherwise False """
|
"""Returns True if photo is a slow motion video, otherwise False"""
|
||||||
return self._info["slow_mo"]
|
return self._info["slow_mo"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def time_lapse(self):
|
def time_lapse(self):
|
||||||
""" Returns True if photo is a time lapse video, otherwise False """
|
"""Returns True if photo is a time lapse video, otherwise False"""
|
||||||
return self._info["time_lapse"]
|
return self._info["time_lapse"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hdr(self):
|
def hdr(self):
|
||||||
""" Returns True if photo is an HDR photo, otherwise False """
|
"""Returns True if photo is an HDR photo, otherwise False"""
|
||||||
return self._info["hdr"]
|
return self._info["hdr"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def screenshot(self):
|
def screenshot(self):
|
||||||
""" Returns True if photo is an HDR photo, otherwise False """
|
"""Returns True if photo is an HDR photo, otherwise False"""
|
||||||
return self._info["screenshot"]
|
return self._info["screenshot"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def portrait(self):
|
def portrait(self):
|
||||||
""" Returns True if photo is a portrait, otherwise False """
|
"""Returns True if photo is a portrait, otherwise False"""
|
||||||
return self._info["portrait"]
|
return self._info["portrait"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selfie(self):
|
def selfie(self):
|
||||||
""" Returns True if photo is a selfie (front facing camera), otherwise False """
|
"""Returns True if photo is a selfie (front facing camera), otherwise False"""
|
||||||
return self._info["selfie"]
|
return self._info["selfie"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def place(self):
|
def place(self):
|
||||||
""" Returns PlaceInfo object containing reverse geolocation info """
|
"""Returns PlaceInfo object containing reverse geolocation info"""
|
||||||
|
|
||||||
# implementation note: doesn't create the PlaceInfo object until requested
|
# implementation note: doesn't create the PlaceInfo object until requested
|
||||||
# then memoizes the object in self._place to avoid recreating the object
|
# then memoizes the object in self._place to avoid recreating the object
|
||||||
@ -938,12 +938,12 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def has_raw(self):
|
def has_raw(self):
|
||||||
""" returns True if photo has an associated raw image (that is, it's a RAW+JPEG pair), otherwise False """
|
"""returns True if photo has an associated raw image (that is, it's a RAW+JPEG pair), otherwise False"""
|
||||||
return self._info["has_raw"]
|
return self._info["has_raw"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def israw(self):
|
def israw(self):
|
||||||
""" returns True if photo is a raw image. For images with an associated RAW+JPEG pair, see has_raw """
|
"""returns True if photo is a raw image. For images with an associated RAW+JPEG pair, see has_raw"""
|
||||||
return "raw-image" in self.uti_original
|
return "raw-image" in self.uti_original
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -955,17 +955,17 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def height(self):
|
def height(self):
|
||||||
""" returns height of the current photo version in pixels """
|
"""returns height of the current photo version in pixels"""
|
||||||
return self._info["height"]
|
return self._info["height"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width(self):
|
def width(self):
|
||||||
""" returns width of the current photo version in pixels """
|
"""returns width of the current photo version in pixels"""
|
||||||
return self._info["width"]
|
return self._info["width"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def orientation(self):
|
def orientation(self):
|
||||||
""" returns EXIF orientation of the current photo version as int or 0 if current orientation cannot be determined """
|
"""returns EXIF orientation of the current photo version as int or 0 if current orientation cannot be determined"""
|
||||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
if self._db._db_version <= _PHOTOS_4_VERSION:
|
||||||
return self._info["orientation"]
|
return self._info["orientation"]
|
||||||
|
|
||||||
@ -981,27 +981,27 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def original_height(self):
|
def original_height(self):
|
||||||
""" returns height of the original photo version in pixels """
|
"""returns height of the original photo version in pixels"""
|
||||||
return self._info["original_height"]
|
return self._info["original_height"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def original_width(self):
|
def original_width(self):
|
||||||
""" returns width of the original photo version in pixels """
|
"""returns width of the original photo version in pixels"""
|
||||||
return self._info["original_width"]
|
return self._info["original_width"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def original_orientation(self):
|
def original_orientation(self):
|
||||||
""" returns EXIF orientation of the original photo version as int """
|
"""returns EXIF orientation of the original photo version as int"""
|
||||||
return self._info["original_orientation"]
|
return self._info["original_orientation"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def original_filesize(self):
|
def original_filesize(self):
|
||||||
""" returns filesize of original photo in bytes as int """
|
"""returns filesize of original photo in bytes as int"""
|
||||||
return self._info["original_filesize"]
|
return self._info["original_filesize"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def duplicates(self):
|
def duplicates(self):
|
||||||
""" return list of PhotoInfo objects for possible duplicates (matching signature of original size, date, height, width) or empty list if no matching duplicates """
|
"""return list of PhotoInfo objects for possible duplicates (matching signature of original size, date, height, width) or empty list if no matching duplicates"""
|
||||||
signature = self._db._duplicate_signature(self.uuid)
|
signature = self._db._duplicate_signature(self.uuid)
|
||||||
duplicates = []
|
duplicates = []
|
||||||
try:
|
try:
|
||||||
@ -1060,12 +1060,12 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def _longitude(self):
|
def _longitude(self):
|
||||||
""" Returns longitude, in degrees """
|
"""Returns longitude, in degrees"""
|
||||||
return self._info["longitude"]
|
return self._info["longitude"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _latitude(self):
|
def _latitude(self):
|
||||||
""" Returns latitude, in degrees """
|
"""Returns latitude, in degrees"""
|
||||||
return self._info["latitude"]
|
return self._info["latitude"]
|
||||||
|
|
||||||
def _get_album_uuids(self):
|
def _get_album_uuids(self):
|
||||||
@ -1103,7 +1103,7 @@ class PhotoInfo:
|
|||||||
return f"osxphotos.{self.__class__.__name__}(db={self._db}, uuid='{self._uuid}', info={self._info})"
|
return f"osxphotos.{self.__class__.__name__}(db={self._db}, uuid='{self._uuid}', info={self._info})"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" string representation of PhotoInfo object """
|
"""string representation of PhotoInfo object"""
|
||||||
|
|
||||||
date_iso = self.date.isoformat()
|
date_iso = self.date.isoformat()
|
||||||
date_modified_iso = (
|
date_modified_iso = (
|
||||||
@ -1166,7 +1166,7 @@ class PhotoInfo:
|
|||||||
return yaml.dump(info, sort_keys=False)
|
return yaml.dump(info, sort_keys=False)
|
||||||
|
|
||||||
def asdict(self):
|
def asdict(self):
|
||||||
""" return dict representation """
|
"""return dict representation"""
|
||||||
|
|
||||||
folders = {album.title: album.folder_names for album in self.album_info}
|
folders = {album.title: album.folder_names for album in self.album_info}
|
||||||
exif = dataclasses.asdict(self.exif_info) if self.exif_info else {}
|
exif = dataclasses.asdict(self.exif_info) if self.exif_info else {}
|
||||||
@ -1242,7 +1242,7 @@ class PhotoInfo:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
""" Return JSON representation """
|
"""Return JSON representation"""
|
||||||
|
|
||||||
def default(o):
|
def default(o):
|
||||||
if isinstance(o, (datetime.date, datetime.datetime)):
|
if isinstance(o, (datetime.date, datetime.datetime)):
|
||||||
@ -1251,7 +1251,7 @@ class PhotoInfo:
|
|||||||
return json.dumps(self.asdict(), sort_keys=True, default=default)
|
return json.dumps(self.asdict(), sort_keys=True, default=default)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
""" Compare two PhotoInfo objects for equality """
|
"""Compare two PhotoInfo objects for equality"""
|
||||||
# Can't just compare the two __dicts__ because some methods (like albums)
|
# Can't just compare the two __dicts__ because some methods (like albums)
|
||||||
# memoize their value once called in an instance variable (e.g. self._albums)
|
# memoize their value once called in an instance variable (e.g. self._albums)
|
||||||
if isinstance(other, self.__class__):
|
if isinstance(other, self.__class__):
|
||||||
@ -1263,5 +1263,9 @@ class PhotoInfo:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
""" Compare two PhotoInfo objects for inequality """
|
"""Compare two PhotoInfo objects for inequality"""
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
"""Make PhotoInfo hashable"""
|
||||||
|
return hash(self.uuid)
|
||||||
|
|||||||
@ -62,7 +62,7 @@ from .photosdb_utils import get_db_model_version, get_db_version
|
|||||||
|
|
||||||
|
|
||||||
class PhotosDB:
|
class PhotosDB:
|
||||||
""" Processes a Photos.app library database to extract information about photos """
|
"""Processes a Photos.app library database to extract information about photos"""
|
||||||
|
|
||||||
# import additional methods
|
# import additional methods
|
||||||
from ._photosdb_process_exif import _process_exifinfo
|
from ._photosdb_process_exif import _process_exifinfo
|
||||||
@ -78,13 +78,13 @@ class PhotosDB:
|
|||||||
from ._photosdb_process_comments import _process_comments
|
from ._photosdb_process_comments import _process_comments
|
||||||
|
|
||||||
def __init__(self, dbfile=None, verbose=None, exiftool=None):
|
def __init__(self, dbfile=None, verbose=None, exiftool=None):
|
||||||
""" Create a new PhotosDB object.
|
"""Create a new PhotosDB object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dbfile: specify full path to photos library or photos.db; if None, will attempt to locate last library opened by Photos.
|
dbfile: specify full path to photos library or photos.db; if None, will attempt to locate last library opened by Photos.
|
||||||
verbose: optional callable function to use for printing verbose text during processing; if None (default), does not print output.
|
verbose: optional callable function to use for printing verbose text during processing; if None (default), does not print output.
|
||||||
exiftool: optional path to exiftool for methods that require this (e.g. PhotoInfo.exiftool); if not provided, will search PATH
|
exiftool: optional path to exiftool for methods that require this (e.g. PhotoInfo.exiftool); if not provided, will search PATH
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
FileNotFoundError if dbfile is not a valid Photos library.
|
FileNotFoundError if dbfile is not a valid Photos library.
|
||||||
TypeError if verbose is not None and not callable.
|
TypeError if verbose is not None and not callable.
|
||||||
@ -326,7 +326,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def keywords_as_dict(self):
|
def keywords_as_dict(self):
|
||||||
""" return keywords as dict of keyword, count in reverse sorted order (descending) """
|
"""return keywords as dict of keyword, count in reverse sorted order (descending)"""
|
||||||
keywords = {
|
keywords = {
|
||||||
k: len(self._dbkeywords_keyword[k]) for k in self._dbkeywords_keyword.keys()
|
k: len(self._dbkeywords_keyword[k]) for k in self._dbkeywords_keyword.keys()
|
||||||
}
|
}
|
||||||
@ -336,7 +336,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def persons_as_dict(self):
|
def persons_as_dict(self):
|
||||||
""" return persons as dict of person, count in reverse sorted order (descending) """
|
"""return persons as dict of person, count in reverse sorted order (descending)"""
|
||||||
persons = {}
|
persons = {}
|
||||||
for pk in self._dbfaces_pk:
|
for pk in self._dbfaces_pk:
|
||||||
fullname = self._dbpersons_pk[pk]["fullname"]
|
fullname = self._dbpersons_pk[pk]["fullname"]
|
||||||
@ -349,7 +349,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def albums_as_dict(self):
|
def albums_as_dict(self):
|
||||||
""" return albums as dict of albums, count in reverse sorted order (descending) """
|
"""return albums as dict of albums, count in reverse sorted order (descending)"""
|
||||||
albums = {}
|
albums = {}
|
||||||
album_keys = self._get_album_uuids(shared=False)
|
album_keys = self._get_album_uuids(shared=False)
|
||||||
for album in album_keys:
|
for album in album_keys:
|
||||||
@ -366,8 +366,8 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def albums_shared_as_dict(self):
|
def albums_shared_as_dict(self):
|
||||||
""" returns shared albums as dict of albums, count in reverse sorted order (descending)
|
"""returns shared albums as dict of albums, count in reverse sorted order (descending)
|
||||||
valid only on Photos 5; on Photos <= 4, prints warning and returns empty dict """
|
valid only on Photos 5; on Photos <= 4, prints warning and returns empty dict"""
|
||||||
|
|
||||||
albums = {}
|
albums = {}
|
||||||
album_keys = self._get_album_uuids(shared=True)
|
album_keys = self._get_album_uuids(shared=True)
|
||||||
@ -385,19 +385,19 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def keywords(self):
|
def keywords(self):
|
||||||
""" return list of keywords found in photos database """
|
"""return list of keywords found in photos database"""
|
||||||
keywords = self._dbkeywords_keyword.keys()
|
keywords = self._dbkeywords_keyword.keys()
|
||||||
return list(keywords)
|
return list(keywords)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def persons(self):
|
def persons(self):
|
||||||
""" return list of persons found in photos database """
|
"""return list of persons found in photos database"""
|
||||||
persons = {self._dbpersons_pk[k]["fullname"] for k in self._dbfaces_pk}
|
persons = {self._dbpersons_pk[k]["fullname"] for k in self._dbfaces_pk}
|
||||||
return list(persons)
|
return list(persons)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def person_info(self):
|
def person_info(self):
|
||||||
""" return list of PersonInfo objects for each person in the photos database """
|
"""return list of PersonInfo objects for each person in the photos database"""
|
||||||
try:
|
try:
|
||||||
return self._person_info
|
return self._person_info
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -408,7 +408,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def folder_info(self):
|
def folder_info(self):
|
||||||
""" return list FolderInfo objects representing top-level folders in the photos database """
|
"""return list FolderInfo objects representing top-level folders in the photos database"""
|
||||||
if self._db_version <= _PHOTOS_4_VERSION:
|
if self._db_version <= _PHOTOS_4_VERSION:
|
||||||
folders = [
|
folders = [
|
||||||
FolderInfo(db=self, uuid=folder)
|
FolderInfo(db=self, uuid=folder)
|
||||||
@ -429,7 +429,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def folders(self):
|
def folders(self):
|
||||||
""" return list of top-level folder names in the photos database """
|
"""return list of top-level folder names in the photos database"""
|
||||||
if self._db_version <= _PHOTOS_4_VERSION:
|
if self._db_version <= _PHOTOS_4_VERSION:
|
||||||
folder_names = [
|
folder_names = [
|
||||||
folder["name"]
|
folder["name"]
|
||||||
@ -450,7 +450,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def album_info(self):
|
def album_info(self):
|
||||||
""" return list of AlbumInfo objects for each album in the photos database """
|
"""return list of AlbumInfo objects for each album in the photos database"""
|
||||||
try:
|
try:
|
||||||
return self._album_info
|
return self._album_info
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -462,8 +462,8 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def album_info_shared(self):
|
def album_info_shared(self):
|
||||||
""" return list of AlbumInfo objects for each shared album in the photos database
|
"""return list of AlbumInfo objects for each shared album in the photos database
|
||||||
only valid for Photos 5; on Photos <= 4, prints warning and returns empty list """
|
only valid for Photos 5; on Photos <= 4, prints warning and returns empty list"""
|
||||||
# if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
|
# if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
|
||||||
try:
|
try:
|
||||||
return self._album_info_shared
|
return self._album_info_shared
|
||||||
@ -476,7 +476,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def albums(self):
|
def albums(self):
|
||||||
""" return list of albums found in photos database """
|
"""return list of albums found in photos database"""
|
||||||
|
|
||||||
# Could be more than one album with same name
|
# Could be more than one album with same name
|
||||||
# Right now, they are treated as same album and photos are combined from albums with same name
|
# Right now, they are treated as same album and photos are combined from albums with same name
|
||||||
@ -489,8 +489,8 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def albums_shared(self):
|
def albums_shared(self):
|
||||||
""" return list of shared albums found in photos database
|
"""return list of shared albums found in photos database
|
||||||
only valid for Photos 5; on Photos <= 4, prints warning and returns empty list """
|
only valid for Photos 5; on Photos <= 4, prints warning and returns empty list"""
|
||||||
|
|
||||||
# Could be more than one album with same name
|
# Could be more than one album with same name
|
||||||
# Right now, they are treated as same album and photos are combined from albums with same name
|
# Right now, they are treated as same album and photos are combined from albums with same name
|
||||||
@ -505,7 +505,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def import_info(self):
|
def import_info(self):
|
||||||
""" return list of ImportInfo objects for each import session in the database """
|
"""return list of ImportInfo objects for each import session in the database"""
|
||||||
try:
|
try:
|
||||||
return self._import_info
|
return self._import_info
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -517,21 +517,21 @@ class PhotosDB:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def db_version(self):
|
def db_version(self):
|
||||||
""" return the database version as stored in LiGlobals table """
|
"""return the database version as stored in LiGlobals table"""
|
||||||
return self._db_version
|
return self._db_version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def db_path(self):
|
def db_path(self):
|
||||||
""" returns path to the Photos library database PhotosDB was initialized with """
|
"""returns path to the Photos library database PhotosDB was initialized with"""
|
||||||
return os.path.abspath(self._dbfile)
|
return os.path.abspath(self._dbfile)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def library_path(self):
|
def library_path(self):
|
||||||
""" returns path to the Photos library PhotosDB was initialized with """
|
"""returns path to the Photos library PhotosDB was initialized with"""
|
||||||
return self._library_path
|
return self._library_path
|
||||||
|
|
||||||
def get_db_connection(self):
|
def get_db_connection(self):
|
||||||
""" Get connection to the working copy of the Photos database
|
"""Get connection to the working copy of the Photos database
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple of (connection, cursor) to sqlite3 database
|
tuple of (connection, cursor) to sqlite3 database
|
||||||
@ -539,7 +539,7 @@ class PhotosDB:
|
|||||||
return _open_sql_file(self._tmp_db)
|
return _open_sql_file(self._tmp_db)
|
||||||
|
|
||||||
def _copy_db_file(self, fname):
|
def _copy_db_file(self, fname):
|
||||||
""" copies the sqlite database file to a temp file """
|
"""copies the sqlite database file to a temp file"""
|
||||||
""" returns the name of the temp file """
|
""" returns the name of the temp file """
|
||||||
""" If sqlite shared memory and write-ahead log files exist, those are copied too """
|
""" If sqlite shared memory and write-ahead log files exist, those are copied too """
|
||||||
# required because python's sqlite3 implementation can't read a locked file
|
# required because python's sqlite3 implementation can't read a locked file
|
||||||
@ -591,8 +591,8 @@ class PhotosDB:
|
|||||||
# return dest_path
|
# return dest_path
|
||||||
|
|
||||||
def _process_database4(self):
|
def _process_database4(self):
|
||||||
""" process the Photos database to extract info
|
"""process the Photos database to extract info
|
||||||
works on Photos version <= 4.0 """
|
works on Photos version <= 4.0"""
|
||||||
|
|
||||||
verbose = self._verbose
|
verbose = self._verbose
|
||||||
verbose("Processing database.")
|
verbose("Processing database.")
|
||||||
@ -1543,15 +1543,15 @@ class PhotosDB:
|
|||||||
logging.debug(pformat(self._dbphotos_burst))
|
logging.debug(pformat(self._dbphotos_burst))
|
||||||
|
|
||||||
def _build_album_folder_hierarchy_4(self, uuid, folders=None):
|
def _build_album_folder_hierarchy_4(self, uuid, folders=None):
|
||||||
""" recursively build folder/album hierarchy
|
"""recursively build folder/album hierarchy
|
||||||
uuid: parent uuid of the album being processed
|
uuid: parent uuid of the album being processed
|
||||||
(parent uuid is a folder in RKFolders)
|
(parent uuid is a folder in RKFolders)
|
||||||
folders: dict holding the folder hierarchy
|
folders: dict holding the folder hierarchy
|
||||||
NOTE: This implementation is different than _build_album_folder_hierarchy_5
|
NOTE: This implementation is different than _build_album_folder_hierarchy_5
|
||||||
which takes the uuid of the album being processed. Here uuid is the parent uuid
|
which takes the uuid of the album being processed. Here uuid is the parent uuid
|
||||||
of the parent folder album because in Photos <=4, folders are in RKFolders and
|
of the parent folder album because in Photos <=4, folders are in RKFolders and
|
||||||
albums in RKAlbums. In Photos 5, folders are just special albums
|
albums in RKAlbums. In Photos 5, folders are just special albums
|
||||||
with kind = _PHOTOS_5_FOLDER_KIND """
|
with kind = _PHOTOS_5_FOLDER_KIND"""
|
||||||
|
|
||||||
parent_uuid = self._dbfolder_details[uuid]["parentFolderUuid"]
|
parent_uuid = self._dbfolder_details[uuid]["parentFolderUuid"]
|
||||||
|
|
||||||
@ -1574,11 +1574,11 @@ class PhotosDB:
|
|||||||
return folders
|
return folders
|
||||||
|
|
||||||
def _process_database5(self):
|
def _process_database5(self):
|
||||||
""" process the Photos database to extract info
|
"""process the Photos database to extract info
|
||||||
works on Photos version 5 and version 6
|
works on Photos version 5 and version 6
|
||||||
|
|
||||||
This is a big hairy 700 line function that should probably be refactored
|
This is a big hairy 700 line function that should probably be refactored
|
||||||
but it works so don't touch it.
|
but it works so don't touch it.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if _debug():
|
if _debug():
|
||||||
@ -2448,9 +2448,9 @@ class PhotosDB:
|
|||||||
logging.debug(pformat(self._dbphotos_burst))
|
logging.debug(pformat(self._dbphotos_burst))
|
||||||
|
|
||||||
def _build_album_folder_hierarchy_5(self, uuid, folders=None):
|
def _build_album_folder_hierarchy_5(self, uuid, folders=None):
|
||||||
""" recursively build folder/album hierarchy
|
"""recursively build folder/album hierarchy
|
||||||
uuid: uuid of the album/folder being processed
|
uuid: uuid of the album/folder being processed
|
||||||
folders: dict holding the folder hierarchy """
|
folders: dict holding the folder hierarchy"""
|
||||||
|
|
||||||
# get parent uuid
|
# get parent uuid
|
||||||
parent = self._dbalbum_details[uuid]["parentfolder"]
|
parent = self._dbalbum_details[uuid]["parentfolder"]
|
||||||
@ -2471,17 +2471,17 @@ class PhotosDB:
|
|||||||
return folders
|
return folders
|
||||||
|
|
||||||
def _album_folder_hierarchy_list(self, album_uuid):
|
def _album_folder_hierarchy_list(self, album_uuid):
|
||||||
""" return appropriate album_folder_hierarchy_list for the _db_version """
|
"""return appropriate album_folder_hierarchy_list for the _db_version"""
|
||||||
if self._db_version <= _PHOTOS_4_VERSION:
|
if self._db_version <= _PHOTOS_4_VERSION:
|
||||||
return self._album_folder_hierarchy_list_4(album_uuid)
|
return self._album_folder_hierarchy_list_4(album_uuid)
|
||||||
else:
|
else:
|
||||||
return self._album_folder_hierarchy_list_5(album_uuid)
|
return self._album_folder_hierarchy_list_5(album_uuid)
|
||||||
|
|
||||||
def _album_folder_hierarchy_list_4(self, album_uuid):
|
def _album_folder_hierarchy_list_4(self, album_uuid):
|
||||||
""" return hierarchical list of folder names album_uuid is contained in
|
"""return hierarchical list of folder names album_uuid is contained in
|
||||||
the folder list is in form:
|
the folder list is in form:
|
||||||
["Top level folder", "sub folder 1", "sub folder 2"]
|
["Top level folder", "sub folder 1", "sub folder 2"]
|
||||||
returns empty list of album is not in any folders """
|
returns empty list of album is not in any folders"""
|
||||||
try:
|
try:
|
||||||
folders = self._dbalbum_folders[album_uuid]
|
folders = self._dbalbum_folders[album_uuid]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -2489,7 +2489,7 @@ class PhotosDB:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def _recurse_folder_hierarchy(folders, hierarchy=[]):
|
def _recurse_folder_hierarchy(folders, hierarchy=[]):
|
||||||
""" recursively walk the folders dict to build list of folder hierarchy """
|
"""recursively walk the folders dict to build list of folder hierarchy"""
|
||||||
if not folders:
|
if not folders:
|
||||||
# empty folder dict (album has no folder hierarchy)
|
# empty folder dict (album has no folder hierarchy)
|
||||||
return []
|
return []
|
||||||
@ -2515,10 +2515,10 @@ class PhotosDB:
|
|||||||
return hierarchy
|
return hierarchy
|
||||||
|
|
||||||
def _album_folder_hierarchy_list_5(self, album_uuid):
|
def _album_folder_hierarchy_list_5(self, album_uuid):
|
||||||
""" return hierarchical list of folder names album_uuid is contained in
|
"""return hierarchical list of folder names album_uuid is contained in
|
||||||
the folder list is in form:
|
the folder list is in form:
|
||||||
["Top level folder", "sub folder 1", "sub folder 2"]
|
["Top level folder", "sub folder 1", "sub folder 2"]
|
||||||
returns empty list of album is not in any folders """
|
returns empty list of album is not in any folders"""
|
||||||
try:
|
try:
|
||||||
folders = self._dbalbum_folders[album_uuid]
|
folders = self._dbalbum_folders[album_uuid]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -2526,7 +2526,7 @@ class PhotosDB:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def _recurse_folder_hierarchy(folders, hierarchy=[]):
|
def _recurse_folder_hierarchy(folders, hierarchy=[]):
|
||||||
""" recursively walk the folders dict to build list of folder hierarchy """
|
"""recursively walk the folders dict to build list of folder hierarchy"""
|
||||||
|
|
||||||
if not folders:
|
if not folders:
|
||||||
# empty folder dict (album has no folder hierarchy)
|
# empty folder dict (album has no folder hierarchy)
|
||||||
@ -2558,15 +2558,15 @@ class PhotosDB:
|
|||||||
return self._album_folder_hierarchy_folderinfo_5(album_uuid)
|
return self._album_folder_hierarchy_folderinfo_5(album_uuid)
|
||||||
|
|
||||||
def _album_folder_hierarchy_folderinfo_4(self, album_uuid):
|
def _album_folder_hierarchy_folderinfo_4(self, album_uuid):
|
||||||
""" return hierarchical list of FolderInfo objects album_uuid is contained in
|
"""return hierarchical list of FolderInfo objects album_uuid is contained in
|
||||||
["Top level folder", "sub folder 1", "sub folder 2"]
|
["Top level folder", "sub folder 1", "sub folder 2"]
|
||||||
returns empty list of album is not in any folders """
|
returns empty list of album is not in any folders"""
|
||||||
# title = photosdb._dbalbum_details[album_uuid]["title"]
|
# title = photosdb._dbalbum_details[album_uuid]["title"]
|
||||||
folders = self._dbalbum_folders[album_uuid]
|
folders = self._dbalbum_folders[album_uuid]
|
||||||
# logging.warning(f"uuid = {album_uuid}, folder = {folders}")
|
# logging.warning(f"uuid = {album_uuid}, folder = {folders}")
|
||||||
|
|
||||||
def _recurse_folder_hierarchy(folders, hierarchy=[]):
|
def _recurse_folder_hierarchy(folders, hierarchy=[]):
|
||||||
""" recursively walk the folders dict to build list of folder hierarchy """
|
"""recursively walk the folders dict to build list of folder hierarchy"""
|
||||||
# logging.warning(f"folders={folders},hierarchy = {hierarchy}")
|
# logging.warning(f"folders={folders},hierarchy = {hierarchy}")
|
||||||
if not folders:
|
if not folders:
|
||||||
# empty folder dict (album has no folder hierarchy)
|
# empty folder dict (album has no folder hierarchy)
|
||||||
@ -2592,14 +2592,14 @@ class PhotosDB:
|
|||||||
return hierarchy
|
return hierarchy
|
||||||
|
|
||||||
def _album_folder_hierarchy_folderinfo_5(self, album_uuid):
|
def _album_folder_hierarchy_folderinfo_5(self, album_uuid):
|
||||||
""" return hierarchical list of FolderInfo objects album_uuid is contained in
|
"""return hierarchical list of FolderInfo objects album_uuid is contained in
|
||||||
["Top level folder", "sub folder 1", "sub folder 2"]
|
["Top level folder", "sub folder 1", "sub folder 2"]
|
||||||
returns empty list of album is not in any folders """
|
returns empty list of album is not in any folders"""
|
||||||
# title = photosdb._dbalbum_details[album_uuid]["title"]
|
# title = photosdb._dbalbum_details[album_uuid]["title"]
|
||||||
folders = self._dbalbum_folders[album_uuid]
|
folders = self._dbalbum_folders[album_uuid]
|
||||||
|
|
||||||
def _recurse_folder_hierarchy(folders, hierarchy=[]):
|
def _recurse_folder_hierarchy(folders, hierarchy=[]):
|
||||||
""" recursively walk the folders dict to build list of folder hierarchy """
|
"""recursively walk the folders dict to build list of folder hierarchy"""
|
||||||
|
|
||||||
if not folders:
|
if not folders:
|
||||||
# empty folder dict (album has no folder hierarchy)
|
# empty folder dict (album has no folder hierarchy)
|
||||||
@ -2624,19 +2624,19 @@ class PhotosDB:
|
|||||||
return hierarchy
|
return hierarchy
|
||||||
|
|
||||||
def _get_album_uuids(self, shared=False, import_session=False):
|
def _get_album_uuids(self, shared=False, import_session=False):
|
||||||
""" Return list of album UUIDs found in photos database
|
"""Return list of album UUIDs found in photos database
|
||||||
|
|
||||||
Filters out albums in the trash and any special album types
|
Filters out albums in the trash and any special album types
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
shared: boolean; if True, returns shared albums, else normal albums
|
shared: boolean; if True, returns shared albums, else normal albums
|
||||||
import_session: boolean, if True, returns import session albums, else normal or shared albums
|
import_session: boolean, if True, returns import session albums, else normal or shared albums
|
||||||
Note: flags (shared, import_session) are mutually exclusive
|
Note: flags (shared, import_session) are mutually exclusive
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: raised if mutually exclusive flags passed
|
ValueError: raised if mutually exclusive flags passed
|
||||||
|
|
||||||
Returns: list of album UUIDs
|
Returns: list of album UUIDs
|
||||||
"""
|
"""
|
||||||
if shared and import_session:
|
if shared and import_session:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@ -2688,14 +2688,14 @@ class PhotosDB:
|
|||||||
return album_list
|
return album_list
|
||||||
|
|
||||||
def _get_albums(self, shared=False):
|
def _get_albums(self, shared=False):
|
||||||
""" Return list of album titles found in photos database
|
"""Return list of album titles found in photos database
|
||||||
Albums may have duplicate titles -- these will be treated as a single album.
|
Albums may have duplicate titles -- these will be treated as a single album.
|
||||||
|
|
||||||
Filters out albums in the trash and any special album types
|
Filters out albums in the trash and any special album types
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
shared: boolean; if True, returns shared albums, else normal albums
|
shared: boolean; if True, returns shared albums, else normal albums
|
||||||
|
|
||||||
Returns: list of album names
|
Returns: list of album names
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -2714,7 +2714,7 @@ 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)
|
||||||
@ -2729,10 +2729,10 @@ class PhotosDB:
|
|||||||
persons: list of persons to search for
|
persons: list of persons to search for
|
||||||
albums: list of album names to search for
|
albums: list of album names to search for
|
||||||
images: if True, returns image files, if False, does not return images; default is True
|
images: if True, returns image files, if False, does not return images; default is True
|
||||||
movies: if True, returns movie files, if False, does not return movies; default is True
|
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)
|
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)
|
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,
|
intrash: if True, returns only images in "Recently deleted items" folder,
|
||||||
if False returns only photos that aren't deleted; default is False
|
if False returns only photos that aren't deleted; default is False
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -2839,7 +2839,7 @@ class PhotosDB:
|
|||||||
return photoinfo
|
return photoinfo
|
||||||
|
|
||||||
def get_photo(self, uuid):
|
def get_photo(self, uuid):
|
||||||
""" Returns a single photo matching uuid
|
"""Returns a single photo matching uuid
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
uuid: the UUID of photo to get
|
uuid: the UUID of photo to get
|
||||||
@ -2854,7 +2854,7 @@ class PhotosDB:
|
|||||||
|
|
||||||
# TODO: add to docs and test
|
# TODO: add to docs and test
|
||||||
def photos_by_uuid(self, uuids):
|
def photos_by_uuid(self, uuids):
|
||||||
""" Returns a list of photos with UUID in uuids.
|
"""Returns a list of photos with UUID in uuids.
|
||||||
Does not generate error if invalid or missing UUID passed.
|
Does not generate error if invalid or missing UUID passed.
|
||||||
This is faster than using PhotosDB.photos if you have list of UUIDs.
|
This is faster than using PhotosDB.photos if you have list of UUIDs.
|
||||||
Returns photos regardless of intrash state.
|
Returns photos regardless of intrash state.
|
||||||
@ -3228,7 +3228,7 @@ class PhotosDB:
|
|||||||
return photos
|
return photos
|
||||||
|
|
||||||
def _duplicate_signature(self, uuid):
|
def _duplicate_signature(self, uuid):
|
||||||
""" Compute a signature for finding possible duplicates """
|
"""Compute a signature for finding possible duplicates"""
|
||||||
return (
|
return (
|
||||||
self._dbphotos[uuid]["original_filesize"],
|
self._dbphotos[uuid]["original_filesize"],
|
||||||
self._dbphotos[uuid]["imageDate"],
|
self._dbphotos[uuid]["imageDate"],
|
||||||
@ -3249,8 +3249,8 @@ class PhotosDB:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
""" Returns number of photos in the database
|
"""Returns number of photos in the database
|
||||||
Includes recently deleted photos and non-selected burst images
|
Includes recently deleted photos and non-selected burst images
|
||||||
"""
|
"""
|
||||||
return len(self._dbphotos)
|
return len(self._dbphotos)
|
||||||
|
|
||||||
@ -3280,4 +3280,4 @@ def _get_photos_by_attribute(photos, attribute, values, ignore_case):
|
|||||||
else:
|
else:
|
||||||
for x in values:
|
for x in values:
|
||||||
photos_search.extend(p for p in photos if x in getattr(p, attribute))
|
photos_search.extend(p for p in photos if x in getattr(p, attribute))
|
||||||
return photos_search
|
return list(set(photos_search))
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -5,9 +5,9 @@
|
|||||||
<key>hostname</key>
|
<key>hostname</key>
|
||||||
<string>Rhets-MacBook-Pro.local</string>
|
<string>Rhets-MacBook-Pro.local</string>
|
||||||
<key>hostuuid</key>
|
<key>hostuuid</key>
|
||||||
<string>9575E48B-8D5F-5654-ABAC-4431B1167324</string>
|
<string>585B80BF-8D1F-55EF-A9E8-6CF4E5523959</string>
|
||||||
<key>pid</key>
|
<key>pid</key>
|
||||||
<integer>86501</integer>
|
<integer>570</integer>
|
||||||
<key>processname</key>
|
<key>processname</key>
|
||||||
<string>photolibraryd</string>
|
<string>photolibraryd</string>
|
||||||
<key>uid</key>
|
<key>uid</key>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
|||||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:Subject": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:TagsList": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:PersonInImage": ["Katie"], "EXIF:GPSLatitude": 41.256566, "EXIF:GPSLongitude": -95.940257, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:Subject": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:TagsList": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:PersonInImage": ["Katie"], "EXIF:GPSLatitude": 41.256566, "EXIF:GPSLongitude": -95.940257, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||||
@ -18,6 +18,7 @@
|
|||||||
<dc:subject>
|
<dc:subject>
|
||||||
<rdf:Bag>
|
<rdf:Bag>
|
||||||
<rdf:li>Kids</rdf:li>
|
<rdf:li>Kids</rdf:li>
|
||||||
|
<rdf:li>Multi Keyword</rdf:li>
|
||||||
<rdf:li>Pumpkin Farm</rdf:li>
|
<rdf:li>Pumpkin Farm</rdf:li>
|
||||||
<rdf:li>Test Album</rdf:li>
|
<rdf:li>Test Album</rdf:li>
|
||||||
</rdf:Bag>
|
</rdf:Bag>
|
||||||
@ -37,6 +38,7 @@
|
|||||||
<digiKam:TagsList>
|
<digiKam:TagsList>
|
||||||
<rdf:Seq>
|
<rdf:Seq>
|
||||||
<rdf:li>Kids</rdf:li>
|
<rdf:li>Kids</rdf:li>
|
||||||
|
<rdf:li>Multi Keyword</rdf:li>
|
||||||
<rdf:li>Pumpkin Farm</rdf:li>
|
<rdf:li>Pumpkin Farm</rdf:li>
|
||||||
<rdf:li>Test Album</rdf:li>
|
<rdf:li>Test Album</rdf:li>
|
||||||
</rdf:Seq>
|
</rdf:Seq>
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:Subject": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:TagsList": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:PersonInImage": ["Katie"], "EXIF:GPSLatitude": 41.256566, "EXIF:GPSLongitude": -95.940257, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:Subject": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:TagsList": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:PersonInImage": ["Katie"], "EXIF:GPSLatitude": 41.256566, "EXIF:GPSLongitude": -95.940257, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||||
@ -19,6 +19,7 @@
|
|||||||
<rdf:Bag>
|
<rdf:Bag>
|
||||||
<rdf:li>2018</rdf:li>
|
<rdf:li>2018</rdf:li>
|
||||||
<rdf:li>Kids</rdf:li>
|
<rdf:li>Kids</rdf:li>
|
||||||
|
<rdf:li>Multi Keyword</rdf:li>
|
||||||
<rdf:li>Pumpkin Farm</rdf:li>
|
<rdf:li>Pumpkin Farm</rdf:li>
|
||||||
<rdf:li>Test Album</rdf:li>
|
<rdf:li>Test Album</rdf:li>
|
||||||
</rdf:Bag>
|
</rdf:Bag>
|
||||||
@ -39,6 +40,7 @@
|
|||||||
<rdf:Seq>
|
<rdf:Seq>
|
||||||
<rdf:li>2018</rdf:li>
|
<rdf:li>2018</rdf:li>
|
||||||
<rdf:li>Kids</rdf:li>
|
<rdf:li>Kids</rdf:li>
|
||||||
|
<rdf:li>Multi Keyword</rdf:li>
|
||||||
<rdf:li>Pumpkin Farm</rdf:li>
|
<rdf:li>Pumpkin Farm</rdf:li>
|
||||||
<rdf:li>Test Album</rdf:li>
|
<rdf:li>Test Album</rdf:li>
|
||||||
</rdf:Seq>
|
</rdf:Seq>
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
[{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", "IPTC:Caption-Abstract": "Bride Wedding day", "IPTC:Keywords": ["AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:Subject": ["AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:TagsList": ["AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:PersonInImage": ["Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:CreateDate": "2019:04:15 14:40:24", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2019:04:15", "IPTC:TimeCreated": "14:40:24-04:00", "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
|
[{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", "IPTC:Caption-Abstract": "Bride Wedding day", "IPTC:Keywords": ["AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:Subject": ["AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:TagsList": ["AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:PersonInImage": ["Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:CreateDate": "2019:04:15 14:40:24", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2019:04:15", "IPTC:TimeCreated": "14:40:24-04:00", "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
|
||||||
@ -20,6 +20,7 @@
|
|||||||
<rdf:li>AlbumInFolder</rdf:li>
|
<rdf:li>AlbumInFolder</rdf:li>
|
||||||
<rdf:li>I have a deleted twin</rdf:li>
|
<rdf:li>I have a deleted twin</rdf:li>
|
||||||
<rdf:li>Maria</rdf:li>
|
<rdf:li>Maria</rdf:li>
|
||||||
|
<rdf:li>Multi Keyword</rdf:li>
|
||||||
<rdf:li>wedding</rdf:li>
|
<rdf:li>wedding</rdf:li>
|
||||||
</rdf:Bag>
|
</rdf:Bag>
|
||||||
</dc:subject>
|
</dc:subject>
|
||||||
@ -40,6 +41,7 @@
|
|||||||
<rdf:li>AlbumInFolder</rdf:li>
|
<rdf:li>AlbumInFolder</rdf:li>
|
||||||
<rdf:li>I have a deleted twin</rdf:li>
|
<rdf:li>I have a deleted twin</rdf:li>
|
||||||
<rdf:li>Maria</rdf:li>
|
<rdf:li>Maria</rdf:li>
|
||||||
|
<rdf:li>Multi Keyword</rdf:li>
|
||||||
<rdf:li>wedding</rdf:li>
|
<rdf:li>wedding</rdf:li>
|
||||||
</rdf:Seq>
|
</rdf:Seq>
|
||||||
</digiKam:TagsList>
|
</digiKam:TagsList>
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
[{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", "IPTC:Caption-Abstract": "Bride Wedding day", "IPTC:Keywords": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:Subject": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:TagsList": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:PersonInImage": ["Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:CreateDate": "2019:04:15 14:40:24", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2019:04:15", "IPTC:TimeCreated": "14:40:24-04:00", "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
|
[{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", "IPTC:Caption-Abstract": "Bride Wedding day", "IPTC:Keywords": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:Subject": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:TagsList": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:PersonInImage": ["Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:CreateDate": "2019:04:15 14:40:24", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2019:04:15", "IPTC:TimeCreated": "14:40:24-04:00", "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
|
||||||
@ -21,6 +21,7 @@
|
|||||||
<rdf:li>Folder1/SubFolder2/AlbumInFolder</rdf:li>
|
<rdf:li>Folder1/SubFolder2/AlbumInFolder</rdf:li>
|
||||||
<rdf:li>I have a deleted twin</rdf:li>
|
<rdf:li>I have a deleted twin</rdf:li>
|
||||||
<rdf:li>Maria</rdf:li>
|
<rdf:li>Maria</rdf:li>
|
||||||
|
<rdf:li>Multi Keyword</rdf:li>
|
||||||
<rdf:li>wedding</rdf:li>
|
<rdf:li>wedding</rdf:li>
|
||||||
</rdf:Bag>
|
</rdf:Bag>
|
||||||
</dc:subject>
|
</dc:subject>
|
||||||
@ -42,6 +43,7 @@
|
|||||||
<rdf:li>Folder1/SubFolder2/AlbumInFolder</rdf:li>
|
<rdf:li>Folder1/SubFolder2/AlbumInFolder</rdf:li>
|
||||||
<rdf:li>I have a deleted twin</rdf:li>
|
<rdf:li>I have a deleted twin</rdf:li>
|
||||||
<rdf:li>Maria</rdf:li>
|
<rdf:li>Maria</rdf:li>
|
||||||
|
<rdf:li>Multi Keyword</rdf:li>
|
||||||
<rdf:li>wedding</rdf:li>
|
<rdf:li>wedding</rdf:li>
|
||||||
</rdf:Seq>
|
</rdf:Seq>
|
||||||
</digiKam:TagsList>
|
</digiKam:TagsList>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
from osxphotos._constants import _UNKNOWN_PERSON
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.15.4.photoslibrary/database/photos.db"
|
PHOTOS_DB = "./tests/Test-10.15.4.photoslibrary/database/photos.db"
|
||||||
@ -61,11 +63,12 @@ UUID_DICT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_folders_1():
|
@pytest.fixture(scope="module")
|
||||||
import osxphotos
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
|
def test_folders_1(photosdb):
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
assert len(folders) == len(TOP_LEVEL_FOLDERS)
|
assert len(folders) == len(TOP_LEVEL_FOLDERS)
|
||||||
@ -75,31 +78,19 @@ def test_folders_1():
|
|||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folder_names():
|
def test_folder_names(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# check folder names
|
# check folder names
|
||||||
folder_names = photosdb.folders
|
folder_names = photosdb.folders
|
||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_len():
|
def test_folders_len(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
|
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_children():
|
def test_folders_children(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -118,11 +109,7 @@ def test_folders_children():
|
|||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_parent():
|
def test_folders_parent(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -135,11 +122,7 @@ def test_folders_parent():
|
|||||||
assert child.parent.uuid == folder.uuid
|
assert child.parent.uuid == folder.uuid
|
||||||
|
|
||||||
|
|
||||||
def test_folders_albums():
|
def test_folders_albums(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -156,11 +139,7 @@ def test_folders_albums():
|
|||||||
########## Test AlbumInfo ##########
|
########## Test AlbumInfo ##########
|
||||||
|
|
||||||
|
|
||||||
def test_albums_1():
|
def test_albums_1(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
assert len(albums) == len(ALBUM_NAMES)
|
assert len(albums) == len(ALBUM_NAMES)
|
||||||
|
|
||||||
@ -169,11 +148,7 @@ def test_albums_1():
|
|||||||
assert sorted(album_names) == sorted(ALBUM_NAMES)
|
assert sorted(album_names) == sorted(ALBUM_NAMES)
|
||||||
|
|
||||||
|
|
||||||
def test_albums_parent():
|
def test_albums_parent(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -181,11 +156,7 @@ def test_albums_parent():
|
|||||||
assert parent == ALBUM_PARENT_DICT[album.title]
|
assert parent == ALBUM_PARENT_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_folder_names():
|
def test_albums_folder_names(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -193,11 +164,7 @@ def test_albums_folder_names():
|
|||||||
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_folders():
|
def test_albums_folders(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
for album in albums:
|
for album in albums:
|
||||||
folders = album.folder_list
|
folders = album.folder_list
|
||||||
@ -205,22 +172,14 @@ def test_albums_folders():
|
|||||||
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_len():
|
def test_albums_len(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
assert len(album) == ALBUM_LEN_DICT[album.title]
|
assert len(album) == ALBUM_LEN_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_photos():
|
def test_albums_photos(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -231,12 +190,9 @@ def test_albums_photos():
|
|||||||
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
|
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_album_dates():
|
def test_album_dates(photosdb):
|
||||||
""" Test album date methods """
|
"""Test album date methods"""
|
||||||
import datetime
|
import datetime
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
album = [a for a in photosdb.album_info if a.uuid == UUID_DICT["album_dates"]][0]
|
album = [a for a in photosdb.album_info if a.uuid == UUID_DICT["album_dates"]][0]
|
||||||
assert album.creation_date == datetime.datetime(
|
assert album.creation_date == datetime.datetime(
|
||||||
@ -271,34 +227,24 @@ def test_album_dates():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_photoinfo_albums():
|
def test_photoinfo_albums(photosdb):
|
||||||
""" Test PhotoInfo.albums """
|
"""Test PhotoInfo.albums"""
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
|
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
|
||||||
|
|
||||||
albums = photos[0].albums
|
albums = photos[0].albums
|
||||||
assert "Pumpkin Farm" in albums
|
assert "Pumpkin Farm" in albums
|
||||||
|
|
||||||
|
|
||||||
def test_photoinfo_albums_2():
|
def test_photoinfo_albums_2(photosdb):
|
||||||
""" Test that PhotoInfo.albums returns only number albums expected """
|
"""Test that PhotoInfo.albums returns only number albums expected"""
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
|
||||||
|
|
||||||
albums = photos[0].albums
|
albums = photos[0].albums
|
||||||
assert len(albums) == 2
|
assert len(albums) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_photoinfo_album_info():
|
def test_photoinfo_album_info(photosdb):
|
||||||
""" test PhotoInfo.album_info """
|
"""test PhotoInfo.album_info"""
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
|
||||||
|
|
||||||
album_info = photos[0].album_info
|
album_info = photos[0].album_info
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
from osxphotos._constants import _UNKNOWN_PERSON
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.13.6.photoslibrary/database/photos.db"
|
PHOTOS_DB = "./tests/Test-10.13.6.photoslibrary/database/photos.db"
|
||||||
@ -42,14 +44,15 @@ ALBUM_PHOTO_UUID_DICT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
|
||||||
|
|
||||||
######### Test FolderInfo ##########
|
######### Test FolderInfo ##########
|
||||||
|
|
||||||
|
|
||||||
def test_folders_1():
|
def test_folders_1(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
folders = photosdb.folders
|
folders = photosdb.folders
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
@ -61,32 +64,20 @@ def test_folders_1():
|
|||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folder_names():
|
def test_folder_names(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# check folder names
|
# check folder names
|
||||||
folder_names = photosdb.folders
|
folder_names = photosdb.folders
|
||||||
assert folder_names == TOP_LEVEL_FOLDERS
|
assert folder_names == TOP_LEVEL_FOLDERS
|
||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_len():
|
def test_folders_len(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
|
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_children():
|
def test_folders_children(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -105,11 +96,7 @@ def test_folders_children():
|
|||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_parent():
|
def test_folders_parent(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -122,11 +109,7 @@ def test_folders_parent():
|
|||||||
assert child.parent.uuid == folder.uuid
|
assert child.parent.uuid == folder.uuid
|
||||||
|
|
||||||
|
|
||||||
def test_folders_albums():
|
def test_folders_albums(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -143,11 +126,7 @@ def test_folders_albums():
|
|||||||
########## Test AlbumInfo ##########
|
########## Test AlbumInfo ##########
|
||||||
|
|
||||||
|
|
||||||
def test_albums_1():
|
def test_albums_1(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
assert len(albums) == len(ALBUM_NAMES)
|
assert len(albums) == len(ALBUM_NAMES)
|
||||||
|
|
||||||
@ -156,11 +135,7 @@ def test_albums_1():
|
|||||||
assert sorted(album_names) == sorted(ALBUM_NAMES)
|
assert sorted(album_names) == sorted(ALBUM_NAMES)
|
||||||
|
|
||||||
|
|
||||||
def test_albums_parent():
|
def test_albums_parent(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -168,11 +143,7 @@ def test_albums_parent():
|
|||||||
assert parent == ALBUM_PARENT_DICT[album.title]
|
assert parent == ALBUM_PARENT_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_folder_names():
|
def test_albums_folder_names(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -180,11 +151,7 @@ def test_albums_folder_names():
|
|||||||
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_folders():
|
def test_albums_folders(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -193,22 +160,14 @@ def test_albums_folders():
|
|||||||
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_len():
|
def test_albums_len(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
assert len(album) == ALBUM_LEN_DICT[album.title]
|
assert len(album) == ALBUM_LEN_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_photos():
|
def test_albums_photos(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -219,10 +178,7 @@ def test_albums_photos():
|
|||||||
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
|
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_photoinfo_albums():
|
def test_photoinfo_albums(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
|
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
|
||||||
|
|
||||||
albums = photos[0].albums
|
albums = photos[0].albums
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
from osxphotos._constants import _UNKNOWN_PERSON
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
|
PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
|
||||||
@ -46,14 +48,16 @@ ALBUM_PHOTO_UUID_DICT = {
|
|||||||
|
|
||||||
UUID_DICT = {"two_albums": "8SOE9s0XQVGsuq4ONohTng"}
|
UUID_DICT = {"two_albums": "8SOE9s0XQVGsuq4ONohTng"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
|
||||||
|
|
||||||
######### Test FolderInfo ##########
|
######### Test FolderInfo ##########
|
||||||
|
|
||||||
|
|
||||||
def test_folders_1():
|
def test_folders_1(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
folders = photosdb.folders
|
folders = photosdb.folders
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
@ -65,32 +69,20 @@ def test_folders_1():
|
|||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folder_names():
|
def test_folder_names(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# check folder names
|
# check folder names
|
||||||
folder_names = photosdb.folders
|
folder_names = photosdb.folders
|
||||||
assert folder_names == TOP_LEVEL_FOLDERS
|
assert folder_names == TOP_LEVEL_FOLDERS
|
||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_len():
|
def test_folders_len(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
|
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_children():
|
def test_folders_children(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -109,11 +101,7 @@ def test_folders_children():
|
|||||||
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
|
||||||
|
|
||||||
|
|
||||||
def test_folders_parent():
|
def test_folders_parent(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -126,11 +114,7 @@ def test_folders_parent():
|
|||||||
assert child.parent.uuid == folder.uuid
|
assert child.parent.uuid == folder.uuid
|
||||||
|
|
||||||
|
|
||||||
def test_folders_albums():
|
def test_folders_albums(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# top level folders
|
# top level folders
|
||||||
folders = photosdb.folder_info
|
folders = photosdb.folder_info
|
||||||
|
|
||||||
@ -147,11 +131,7 @@ def test_folders_albums():
|
|||||||
########## Test AlbumInfo ##########
|
########## Test AlbumInfo ##########
|
||||||
|
|
||||||
|
|
||||||
def test_albums_1():
|
def test_albums_1(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
assert len(albums) == 4
|
assert len(albums) == 4
|
||||||
|
|
||||||
@ -160,11 +140,7 @@ def test_albums_1():
|
|||||||
assert sorted(album_names) == sorted(ALBUM_NAMES)
|
assert sorted(album_names) == sorted(ALBUM_NAMES)
|
||||||
|
|
||||||
|
|
||||||
def test_albums_parent():
|
def test_albums_parent(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -172,11 +148,7 @@ def test_albums_parent():
|
|||||||
assert parent == ALBUM_PARENT_DICT[album.title]
|
assert parent == ALBUM_PARENT_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_folder_names():
|
def test_albums_folder_names(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -184,11 +156,7 @@ def test_albums_folder_names():
|
|||||||
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_folders():
|
def test_albums_folders(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -197,22 +165,14 @@ def test_albums_folders():
|
|||||||
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_len():
|
def test_albums_len(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
assert len(album) == ALBUM_LEN_DICT[album.title]
|
assert len(album) == ALBUM_LEN_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_albums_photos():
|
def test_albums_photos(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
albums = photosdb.album_info
|
albums = photosdb.album_info
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
@ -223,20 +183,14 @@ def test_albums_photos():
|
|||||||
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
|
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
|
||||||
|
|
||||||
|
|
||||||
def test_photoinfo_albums():
|
def test_photoinfo_albums(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
|
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
|
||||||
|
|
||||||
albums = photos[0].albums
|
albums = photos[0].albums
|
||||||
assert "Pumpkin Farm" in albums
|
assert "Pumpkin Farm" in albums
|
||||||
|
|
||||||
|
|
||||||
def test_photoinfo_album_info():
|
def test_photoinfo_album_info(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
|
||||||
|
|
||||||
album_info = photos[0].album_info
|
album_info = photos[0].album_info
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,830 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
|
||||||
|
|
||||||
# TODO: put some of this code into a pre-function
|
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.15.1.photoslibrary/database/photos.db"
|
|
||||||
PHOTOS_DB_PATH = "/Test-10.15.1.photoslibrary/database/photos.db"
|
|
||||||
PHOTOS_LIBRARY_PATH = "/Test-10.15.1.photoslibrary"
|
|
||||||
|
|
||||||
KEYWORDS = [
|
|
||||||
"Kids",
|
|
||||||
"wedding",
|
|
||||||
"flowers",
|
|
||||||
"England",
|
|
||||||
"London",
|
|
||||||
"London 2018",
|
|
||||||
"St. James's Park",
|
|
||||||
"UK",
|
|
||||||
"United Kingdom",
|
|
||||||
]
|
|
||||||
# Photos 5 includes blank person for detected face
|
|
||||||
PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON]
|
|
||||||
ALBUMS = [
|
|
||||||
"Pumpkin Farm",
|
|
||||||
"Test Album",
|
|
||||||
"Multi Keyword",
|
|
||||||
] # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
|
||||||
KEYWORDS_DICT = {
|
|
||||||
"Kids": 4,
|
|
||||||
"wedding": 2,
|
|
||||||
"flowers": 1,
|
|
||||||
"England": 1,
|
|
||||||
"London": 1,
|
|
||||||
"London 2018": 1,
|
|
||||||
"St. James's Park": 1,
|
|
||||||
"UK": 1,
|
|
||||||
"United Kingdom": 1,
|
|
||||||
}
|
|
||||||
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1, _UNKNOWN_PERSON: 1}
|
|
||||||
ALBUM_DICT = {
|
|
||||||
"Pumpkin Farm": 3,
|
|
||||||
"Test Album": 2,
|
|
||||||
"Multi Keyword": 2,
|
|
||||||
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
|
||||||
|
|
||||||
UUID_DICT = {
|
|
||||||
"missing": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
|
||||||
"favorite": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
"not_favorite": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
|
||||||
"hidden": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
|
||||||
"not_hidden": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
"has_adjustments": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
"no_adjustments": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
|
||||||
"location": "DC99FBDD-7A52-4100-A5BB-344131646C30",
|
|
||||||
"no_location": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
|
||||||
"external_edit": "DC99FBDD-7A52-4100-A5BB-344131646C30",
|
|
||||||
"no_external_edit": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
"export": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
|
|
||||||
"multi_query_1": "D79B8D77-BFFC-460B-9312-034F2877D35B",
|
|
||||||
"multi_query_2": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_init1():
|
|
||||||
# test named argument
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
|
||||||
|
|
||||||
|
|
||||||
def test_init2():
|
|
||||||
# test positional argument
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
|
||||||
|
|
||||||
|
|
||||||
def test_init3():
|
|
||||||
# test positional and named argument (raises exception)
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
assert osxphotos.PhotosDB(PHOTOS_DB, dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
|
|
||||||
def test_init4():
|
|
||||||
# test invalid db
|
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(bad_db, bad_db_name) = tempfile.mkstemp(suffix=".db", prefix="osxphotos-")
|
|
||||||
os.close(bad_db)
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
assert osxphotos.PhotosDB(bad_db_name)
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
assert osxphotos.PhotosDB(dbfile=bad_db_name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.remove(bad_db_name)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def test_init5(mocker):
|
|
||||||
# test failed get_last_library_path
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
def bad_library():
|
|
||||||
return None
|
|
||||||
|
|
||||||
# get_last_library actually in utils but need to patch it in photosdb because it's imported into photosdb
|
|
||||||
# because of the layout of photosdb/ need to patch it this way...don't really understand why, but it works
|
|
||||||
mocker.patch("osxphotos.photosdb.photosdb.get_last_library_path", new=bad_library)
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
assert osxphotos.PhotosDB()
|
|
||||||
|
|
||||||
|
|
||||||
def test_db_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
|
||||||
assert photosdb.db_version == "6000"
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "Katie" in photosdb.persons
|
|
||||||
assert collections.Counter(PERSONS) == collections.Counter(photosdb.persons)
|
|
||||||
|
|
||||||
|
|
||||||
def test_keywords():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "wedding" in photosdb.keywords
|
|
||||||
assert collections.Counter(KEYWORDS) == collections.Counter(photosdb.keywords)
|
|
||||||
|
|
||||||
|
|
||||||
def test_album_names():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "Pumpkin Farm" in photosdb.albums
|
|
||||||
assert collections.Counter(ALBUMS) == collections.Counter(photosdb.albums)
|
|
||||||
|
|
||||||
|
|
||||||
def test_keywords_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
keywords = photosdb.keywords_as_dict
|
|
||||||
assert keywords["wedding"] == 2
|
|
||||||
assert keywords == KEYWORDS_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons_as_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
persons = photosdb.persons_as_dict
|
|
||||||
assert persons["Maria"] == 1
|
|
||||||
assert persons == PERSONS_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_albums_as_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
albums = photosdb.albums_as_dict
|
|
||||||
assert albums["Pumpkin Farm"] == 3
|
|
||||||
assert albums == ALBUM_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_attributes():
|
|
||||||
import datetime
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["D79B8D77-BFFC-460B-9312-034F2877D35B"])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.keywords == ["Kids"]
|
|
||||||
assert p.original_filename == "Pumkins2.jpg"
|
|
||||||
assert p.filename == "D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
|
||||||
assert p.date == datetime.datetime(
|
|
||||||
2018, 9, 28, 16, 7, 7, 0, datetime.timezone(datetime.timedelta(seconds=-14400))
|
|
||||||
)
|
|
||||||
assert p.description == "Girl holding pumpkin"
|
|
||||||
assert p.title == "I found one!"
|
|
||||||
assert sorted(p.albums) == ["Multi Keyword", "Pumpkin Farm", "Test Album"]
|
|
||||||
assert p.persons == ["Katie"]
|
|
||||||
assert p.path.endswith(
|
|
||||||
"tests/Test-10.15.1.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
|
||||||
)
|
|
||||||
assert p.ismissing == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_missing():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.path == None
|
|
||||||
assert p.ismissing == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_favorite():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.favorite == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_favorite():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_favorite"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.favorite == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_hidden():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["hidden"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.hidden == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_hidden():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.hidden == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_location_1():
|
|
||||||
# test photo with lat/lon info
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["location"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
lat, lon = p.location
|
|
||||||
assert lat == pytest.approx(51.50357167)
|
|
||||||
assert lon == pytest.approx(-0.1318055)
|
|
||||||
|
|
||||||
|
|
||||||
def test_location_2():
|
|
||||||
# test photo with no location info
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_location"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
lat, lon = p.location
|
|
||||||
assert lat is None
|
|
||||||
assert lon is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_hasadjustments1():
|
|
||||||
# test hasadjustments == True
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.hasadjustments == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_hasadjustments2():
|
|
||||||
# test hasadjustments == False
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.hasadjustments == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_external_edit1():
|
|
||||||
# test image has been edited in external editor
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["external_edit"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
|
|
||||||
assert p.external_edit == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_external_edit2():
|
|
||||||
# test image has not been edited in external editor
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_external_edit"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
|
|
||||||
assert p.external_edit == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_path_edited1():
|
|
||||||
# test a valid edited path
|
|
||||||
import os.path
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51"])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
path = p.path_edited
|
|
||||||
assert path.endswith(
|
|
||||||
"resources/renders/E/E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51_1_201_a.jpeg"
|
|
||||||
)
|
|
||||||
assert os.path.exists(path)
|
|
||||||
|
|
||||||
|
|
||||||
def test_path_edited2():
|
|
||||||
# test an invalid edited path
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["6191423D-8DB8-4D4C-92BE-9BBBA308AAC4"])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
path = p.path_edited
|
|
||||||
assert path is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_count():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos()
|
|
||||||
assert len(photos) == 7
|
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_2():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(keywords=["wedding"])
|
|
||||||
assert len(photos) == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_not_in_album():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
|
|
||||||
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
|
|
||||||
photos2 = photosdb.photos(keywords=["Kids"])
|
|
||||||
photos3 = [p for p in photos2 if p not in photos1]
|
|
||||||
assert len(photos3) == 1
|
|
||||||
assert photos3[0].uuid == "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C"
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_db_path():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
db_path = photosdb.db_path
|
|
||||||
assert db_path.endswith(PHOTOS_DB_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_library_path():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
lib_path = photosdb.library_path
|
|
||||||
assert lib_path.endswith(PHOTOS_LIBRARY_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_1():
|
|
||||||
# test basic export
|
|
||||||
# get an unedited image and export it using default filename
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
got_dest = photos[0].export(dest)[0]
|
|
||||||
|
|
||||||
assert got_dest == expected_dest
|
|
||||||
assert os.path.isfile(got_dest)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_2():
|
|
||||||
# test export with user provided filename
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
got_dest = photos[0].export(dest, filename)[0]
|
|
||||||
|
|
||||||
assert got_dest == expected_dest
|
|
||||||
assert os.path.isfile(got_dest)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_3():
|
|
||||||
# test file already exists and test increment=True (default)
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
|
||||||
filename2 = pathlib.Path(filename)
|
|
||||||
filename2 = f"{filename2.stem} (1){filename2.suffix}"
|
|
||||||
expected_dest_2 = os.path.join(dest, filename2)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest)[0]
|
|
||||||
got_dest_2 = photos[0].export(dest)[0]
|
|
||||||
|
|
||||||
assert got_dest_2 == expected_dest_2
|
|
||||||
assert os.path.isfile(got_dest_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_4():
|
|
||||||
# test user supplied file already exists and test increment=True (default)
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
|
|
||||||
filename2 = f"osxphotos-export-2-test-{timestamp} (1).jpg"
|
|
||||||
expected_dest_2 = os.path.join(dest, filename2)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest, filename)[0]
|
|
||||||
got_dest_2 = photos[0].export(dest, filename)[0]
|
|
||||||
|
|
||||||
assert got_dest_2 == expected_dest_2
|
|
||||||
assert os.path.isfile(got_dest_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_5():
|
|
||||||
# test file already exists and test increment=True (default)
|
|
||||||
# and overwrite = True
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest)[0]
|
|
||||||
got_dest_2 = photos[0].export(dest, overwrite=True)[0]
|
|
||||||
|
|
||||||
assert got_dest_2 == got_dest
|
|
||||||
assert got_dest_2 == expected_dest
|
|
||||||
assert os.path.isfile(got_dest_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_6():
|
|
||||||
# test user supplied file already exists and test increment=True (default)
|
|
||||||
# and overwrite = True
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest, filename)[0]
|
|
||||||
got_dest_2 = photos[0].export(dest, filename, overwrite=True)[0]
|
|
||||||
|
|
||||||
assert got_dest_2 == got_dest
|
|
||||||
assert got_dest_2 == expected_dest
|
|
||||||
assert os.path.isfile(got_dest_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_7():
|
|
||||||
# test file already exists and test increment=False (not default), overwrite=False (default)
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].filename
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest)[0]
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
# try to export again with increment = False
|
|
||||||
assert photos[0].export(dest, increment=False)
|
|
||||||
assert e.type == type(FileExistsError())
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_8():
|
|
||||||
# try to export missing file
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
|
||||||
|
|
||||||
filename = photos[0].filename
|
|
||||||
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
assert photos[0].export(dest)[0]
|
|
||||||
assert e.type == type(FileNotFoundError())
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_9():
|
|
||||||
# try to export edited file that's not edited
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
|
||||||
|
|
||||||
filename = photos[0].filename
|
|
||||||
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
assert photos[0].export(dest, edited=True)
|
|
||||||
assert e.type == ValueError
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_10():
|
|
||||||
# try to export edited file that's not edited and name provided
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
|
||||||
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
assert photos[0].export(dest, filename, edited=True)
|
|
||||||
assert e.type == ValueError
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_11():
|
|
||||||
# export edited file with name provided
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest, filename, edited=True)[0]
|
|
||||||
assert got_dest == expected_dest
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_12():
|
|
||||||
# export edited file with default name
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
|
||||||
|
|
||||||
edited_name = pathlib.Path(photos[0].path_edited).name
|
|
||||||
edited_suffix = pathlib.Path(edited_name).suffix
|
|
||||||
filename = pathlib.Path(photos[0].original_filename).stem + "_edited" + edited_suffix
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest, edited=True)[0]
|
|
||||||
assert got_dest == expected_dest
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_13():
|
|
||||||
# export to invalid destination
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
|
|
||||||
# create a folder that doesn't exist
|
|
||||||
i = 0
|
|
||||||
while os.path.isdir(dest):
|
|
||||||
dest = os.path.join(dest, str(i))
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].filename
|
|
||||||
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
assert photos[0].export(dest)
|
|
||||||
assert e.type == type(FileNotFoundError())
|
|
||||||
|
|
||||||
|
|
||||||
def test_eq():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
photos2 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
assert photos1[0] == photos2[0]
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_eq():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
photos2 = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
|
||||||
assert photos1[0] != photos2[0]
|
|
||||||
|
|
||||||
|
|
||||||
def test_photosdb_repr():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photosdb2 = eval(repr(photosdb))
|
|
||||||
|
|
||||||
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name"]
|
|
||||||
assert {k: v for k, v in photosdb.__dict__.items() if k not in ignore_keys} == {
|
|
||||||
k: v for k, v in photosdb2.__dict__.items() if k not in ignore_keys
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_photosinfo_repr():
|
|
||||||
import osxphotos
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
|
|
||||||
photo = photos[0]
|
|
||||||
photo2 = eval(repr(photo))
|
|
||||||
|
|
||||||
assert {k: str(v).encode("utf-8") for k, v in photo.__dict__.items()} == {
|
|
||||||
k: str(v).encode("utf-8") for k, v in photo2.__dict__.items()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_from_to_date():
|
|
||||||
import osxphotos
|
|
||||||
import datetime as dt
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
os.environ["TZ"] = "US/Pacific"
|
|
||||||
time.tzset()
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def test_multi_uuid():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"], UUID_DICT["not_favorite"]])
|
|
||||||
|
|
||||||
assert len(photos) == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_multi_keyword():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(keywords=["Kids", "wedding"])
|
|
||||||
|
|
||||||
assert len(photos) == 6
|
|
||||||
|
|
||||||
|
|
||||||
def test_multi_album():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(albums=["Pumpkin Farm", "Test Album"])
|
|
||||||
|
|
||||||
assert len(photos) == 3
|
|
||||||
|
|
||||||
|
|
||||||
def test_multi_person():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(persons=["Katie", "Suzy"])
|
|
||||||
|
|
||||||
assert len(photos) == 3
|
|
||||||
|
|
||||||
|
|
||||||
def test_compound_query():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(persons=["Katie", "Maria"], albums=["Multi Keyword"])
|
|
||||||
|
|
||||||
assert len(photos) == 2
|
|
||||||
assert UUID_DICT["multi_query_1"] in [p.uuid for p in photos]
|
|
||||||
assert UUID_DICT["multi_query_2"] in [p.uuid for p in photos]
|
|
||||||
@ -1,791 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
|
||||||
|
|
||||||
# TODO: put some of this code into a pre-function
|
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.15.4.photoslibrary/database/photos.db"
|
|
||||||
PHOTOS_DB_PATH = "/Test-10.15.4.photoslibrary/database/photos.db"
|
|
||||||
PHOTOS_LIBRARY_PATH = "/Test-10.15.4.photoslibrary"
|
|
||||||
|
|
||||||
KEYWORDS = [
|
|
||||||
"Kids",
|
|
||||||
"wedding",
|
|
||||||
"flowers",
|
|
||||||
"England",
|
|
||||||
"London",
|
|
||||||
"London 2018",
|
|
||||||
"St. James's Park",
|
|
||||||
"UK",
|
|
||||||
"United Kingdom",
|
|
||||||
]
|
|
||||||
# Photos 5 includes blank person for detected face
|
|
||||||
PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON]
|
|
||||||
ALBUMS = [
|
|
||||||
"Pumpkin Farm",
|
|
||||||
"Test Album",
|
|
||||||
"AlbumInFolder",
|
|
||||||
"Raw",
|
|
||||||
] # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
|
||||||
KEYWORDS_DICT = {
|
|
||||||
"Kids": 4,
|
|
||||||
"wedding": 2,
|
|
||||||
"flowers": 1,
|
|
||||||
"England": 1,
|
|
||||||
"London": 1,
|
|
||||||
"London 2018": 1,
|
|
||||||
"St. James's Park": 1,
|
|
||||||
"UK": 1,
|
|
||||||
"United Kingdom": 1,
|
|
||||||
}
|
|
||||||
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1, _UNKNOWN_PERSON: 1}
|
|
||||||
ALBUM_DICT = {
|
|
||||||
"Pumpkin Farm": 3,
|
|
||||||
"Test Album": 2,
|
|
||||||
"AlbumInFolder": 2,
|
|
||||||
"Raw": 4,
|
|
||||||
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
|
||||||
|
|
||||||
UUID_DICT = {
|
|
||||||
"missing": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
|
||||||
"favorite": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
"not_favorite": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
|
||||||
"hidden": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
|
||||||
"not_hidden": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
"has_adjustments": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
"no_adjustments": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
|
||||||
"location": "DC99FBDD-7A52-4100-A5BB-344131646C30",
|
|
||||||
"no_location": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
|
||||||
"external_edit": "DC99FBDD-7A52-4100-A5BB-344131646C30",
|
|
||||||
"no_external_edit": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
|
||||||
"export": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_init1():
|
|
||||||
# test named argument
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
|
||||||
|
|
||||||
|
|
||||||
def test_init2():
|
|
||||||
# test positional argument
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
|
||||||
|
|
||||||
|
|
||||||
def test_init3():
|
|
||||||
# test positional and named argument (raises exception)
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
assert osxphotos.PhotosDB(PHOTOS_DB, dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
|
|
||||||
def test_init4():
|
|
||||||
# test invalid db
|
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
(bad_db, bad_db_name) = tempfile.mkstemp(suffix=".db", prefix="osxphotos-")
|
|
||||||
os.close(bad_db)
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
assert osxphotos.PhotosDB(bad_db_name)
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
assert osxphotos.PhotosDB(dbfile=bad_db_name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.remove(bad_db_name)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def test_init5(mocker):
|
|
||||||
# test failed get_last_library_path
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
def bad_library():
|
|
||||||
return None
|
|
||||||
|
|
||||||
# get_last_library actually in utils but need to patch it in photosdb because it's imported into photosdb
|
|
||||||
# because of the layout of photosdb/ need to patch it this way...don't really understand why, but it works
|
|
||||||
mocker.patch("osxphotos.photosdb.photosdb.get_last_library_path", new=bad_library)
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
assert osxphotos.PhotosDB()
|
|
||||||
|
|
||||||
|
|
||||||
def test_db_len():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
|
||||||
assert len(photosdb) == 12
|
|
||||||
|
|
||||||
|
|
||||||
def test_db_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
|
||||||
assert photosdb.db_version == "6000"
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "Katie" in photosdb.persons
|
|
||||||
assert collections.Counter(PERSONS) == collections.Counter(photosdb.persons)
|
|
||||||
|
|
||||||
|
|
||||||
def test_keywords():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "wedding" in photosdb.keywords
|
|
||||||
assert collections.Counter(KEYWORDS) == collections.Counter(photosdb.keywords)
|
|
||||||
|
|
||||||
|
|
||||||
def test_album_names():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "Pumpkin Farm" in photosdb.albums
|
|
||||||
assert collections.Counter(ALBUMS) == collections.Counter(photosdb.albums)
|
|
||||||
|
|
||||||
|
|
||||||
def test_keywords_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
keywords = photosdb.keywords_as_dict
|
|
||||||
assert keywords["wedding"] == 2
|
|
||||||
assert keywords == KEYWORDS_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons_as_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
persons = photosdb.persons_as_dict
|
|
||||||
assert persons["Maria"] == 1
|
|
||||||
assert persons == PERSONS_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_albums_as_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
albums = photosdb.albums_as_dict
|
|
||||||
assert albums["Pumpkin Farm"] == 3
|
|
||||||
assert albums == ALBUM_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_attributes():
|
|
||||||
import datetime
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["D79B8D77-BFFC-460B-9312-034F2877D35B"])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.keywords == ["Kids"]
|
|
||||||
assert p.original_filename == "Pumkins2.jpg"
|
|
||||||
assert p.filename == "D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
|
||||||
assert p.date == datetime.datetime(
|
|
||||||
2018, 9, 28, 16, 7, 7, 0, datetime.timezone(datetime.timedelta(seconds=-14400))
|
|
||||||
)
|
|
||||||
assert p.description == "Girl holding pumpkin"
|
|
||||||
assert p.title == "I found one!"
|
|
||||||
assert sorted(p.albums) == ["Pumpkin Farm", "Test Album"]
|
|
||||||
assert p.persons == ["Katie"]
|
|
||||||
assert p.path.endswith(
|
|
||||||
"tests/Test-10.15.4.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
|
||||||
)
|
|
||||||
assert p.ismissing == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_missing():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.path == None
|
|
||||||
assert p.ismissing == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_favorite():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.favorite == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_favorite():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_favorite"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.favorite == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_hidden():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["hidden"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.hidden == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_hidden():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.hidden == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_location_1():
|
|
||||||
# test photo with lat/lon info
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["location"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
lat, lon = p.location
|
|
||||||
assert lat == pytest.approx(51.50357167)
|
|
||||||
assert lon == pytest.approx(-0.1318055)
|
|
||||||
|
|
||||||
|
|
||||||
def test_location_2():
|
|
||||||
# test photo with no location info
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_location"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
lat, lon = p.location
|
|
||||||
assert lat is None
|
|
||||||
assert lon is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_hasadjustments1():
|
|
||||||
# test hasadjustments == True
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.hasadjustments == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_hasadjustments2():
|
|
||||||
# test hasadjustments == False
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.hasadjustments == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_external_edit1():
|
|
||||||
# test image has been edited in external editor
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["external_edit"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
|
|
||||||
assert p.external_edit == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_external_edit2():
|
|
||||||
# test image has not been edited in external editor
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_external_edit"]])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
|
|
||||||
assert p.external_edit == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_path_edited1():
|
|
||||||
# test a valid edited path
|
|
||||||
import os.path
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51"])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
path = p.path_edited
|
|
||||||
assert path.endswith(
|
|
||||||
"resources/renders/E/E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51_1_201_a.jpeg"
|
|
||||||
)
|
|
||||||
assert os.path.exists(path)
|
|
||||||
|
|
||||||
|
|
||||||
def test_path_edited2():
|
|
||||||
# test an invalid edited path
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["6191423D-8DB8-4D4C-92BE-9BBBA308AAC4"])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
path = p.path_edited
|
|
||||||
assert path is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_count():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos()
|
|
||||||
assert len(photos) == 12
|
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_2():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(keywords=["wedding"])
|
|
||||||
assert len(photos) == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_not_in_album():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
|
|
||||||
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
|
|
||||||
photos2 = photosdb.photos(keywords=["Kids"])
|
|
||||||
photos3 = [p for p in photos2 if p not in photos1]
|
|
||||||
assert len(photos3) == 1
|
|
||||||
assert photos3[0].uuid == "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C"
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_db_path():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
db_path = photosdb.db_path
|
|
||||||
assert db_path.endswith(PHOTOS_DB_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_library_path():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
lib_path = photosdb.library_path
|
|
||||||
assert lib_path.endswith(PHOTOS_LIBRARY_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_1():
|
|
||||||
# test basic export
|
|
||||||
# get an unedited image and export it using default filename
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
got_dest = photos[0].export(dest)[0]
|
|
||||||
|
|
||||||
assert got_dest == expected_dest
|
|
||||||
assert os.path.isfile(got_dest)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_2():
|
|
||||||
# test export with user provided filename
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
got_dest = photos[0].export(dest, filename)[0]
|
|
||||||
|
|
||||||
assert got_dest == expected_dest
|
|
||||||
assert os.path.isfile(got_dest)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_3():
|
|
||||||
# test file already exists and test increment=True (default)
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
|
||||||
filename2 = pathlib.Path(filename)
|
|
||||||
filename2 = f"{filename2.stem} (1){filename2.suffix}"
|
|
||||||
expected_dest_2 = os.path.join(dest, filename2)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest)[0]
|
|
||||||
got_dest_2 = photos[0].export(dest)[0]
|
|
||||||
|
|
||||||
assert got_dest_2 == expected_dest_2
|
|
||||||
assert os.path.isfile(got_dest_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_4():
|
|
||||||
# test user supplied file already exists and test increment=True (default)
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
|
|
||||||
filename2 = f"osxphotos-export-2-test-{timestamp} (1).jpg"
|
|
||||||
expected_dest_2 = os.path.join(dest, filename2)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest, filename)[0]
|
|
||||||
got_dest_2 = photos[0].export(dest, filename)[0]
|
|
||||||
|
|
||||||
assert got_dest_2 == expected_dest_2
|
|
||||||
assert os.path.isfile(got_dest_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_5():
|
|
||||||
# test file already exists and test increment=True (default)
|
|
||||||
# and overwrite = True
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest)[0]
|
|
||||||
got_dest_2 = photos[0].export(dest, overwrite=True)[0]
|
|
||||||
|
|
||||||
assert got_dest_2 == got_dest
|
|
||||||
assert got_dest_2 == expected_dest
|
|
||||||
assert os.path.isfile(got_dest_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_6():
|
|
||||||
# test user supplied file already exists and test increment=True (default)
|
|
||||||
# and overwrite = True
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest, filename)[0]
|
|
||||||
got_dest_2 = photos[0].export(dest, filename, overwrite=True)[0]
|
|
||||||
|
|
||||||
assert got_dest_2 == got_dest
|
|
||||||
assert got_dest_2 == expected_dest
|
|
||||||
assert os.path.isfile(got_dest_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_7():
|
|
||||||
# test file already exists and test increment=False (not default), overwrite=False (default)
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].filename
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest)[0]
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
# try to export again with increment = False
|
|
||||||
assert photos[0].export(dest, increment=False)
|
|
||||||
assert e.type == type(FileExistsError())
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_8():
|
|
||||||
# try to export missing file
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
|
||||||
|
|
||||||
filename = photos[0].filename
|
|
||||||
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
assert photos[0].export(dest)[0]
|
|
||||||
assert e.type == type(FileNotFoundError())
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_9():
|
|
||||||
# try to export edited file that's not edited
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
|
||||||
|
|
||||||
filename = photos[0].filename
|
|
||||||
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
assert photos[0].export(dest, edited=True)
|
|
||||||
assert e.type == ValueError
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_10():
|
|
||||||
# try to export edited file that's not edited and name provided
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
|
||||||
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
assert photos[0].export(dest, filename, edited=True)
|
|
||||||
assert e.type == ValueError
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_11():
|
|
||||||
# export edited file with name provided
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
|
||||||
|
|
||||||
timestamp = time.time()
|
|
||||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest, filename, edited=True)[0]
|
|
||||||
assert got_dest == expected_dest
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_12():
|
|
||||||
# export edited file with default name
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
|
||||||
|
|
||||||
edited_name = pathlib.Path(photos[0].path_edited).name
|
|
||||||
edited_suffix = pathlib.Path(edited_name).suffix
|
|
||||||
filename = pathlib.Path(photos[0].original_filename).stem + "_edited" + edited_suffix
|
|
||||||
expected_dest = os.path.join(dest, filename)
|
|
||||||
|
|
||||||
got_dest = photos[0].export(dest, edited=True)[0]
|
|
||||||
assert got_dest == expected_dest
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_13():
|
|
||||||
# export to invalid destination
|
|
||||||
# should raise exception
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
|
||||||
dest = tempdir.name
|
|
||||||
|
|
||||||
# create a folder that doesn't exist
|
|
||||||
i = 0
|
|
||||||
while os.path.isdir(dest):
|
|
||||||
dest = os.path.join(dest, str(i))
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
|
|
||||||
filename = photos[0].filename
|
|
||||||
|
|
||||||
with pytest.raises(Exception) as e:
|
|
||||||
assert photos[0].export(dest)
|
|
||||||
assert e.type == type(FileNotFoundError())
|
|
||||||
|
|
||||||
|
|
||||||
def test_eq():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
photos2 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
assert photos1[0] == photos2[0]
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_eq():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
|
||||||
photos2 = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
|
||||||
assert photos1[0] != photos2[0]
|
|
||||||
|
|
||||||
|
|
||||||
def test_photosdb_repr():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photosdb2 = eval(repr(photosdb))
|
|
||||||
|
|
||||||
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name"]
|
|
||||||
assert {k: v for k, v in photosdb.__dict__.items() if k not in ignore_keys} == {
|
|
||||||
k: v for k, v in photosdb2.__dict__.items() if k not in ignore_keys
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_photosinfo_repr():
|
|
||||||
import osxphotos
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
|
|
||||||
photo = photos[0]
|
|
||||||
photo2 = eval(repr(photo))
|
|
||||||
|
|
||||||
assert {k: str(v).encode("utf-8") for k, v in photo.__dict__.items()} == {
|
|
||||||
k: str(v).encode("utf-8") for k, v in photo2.__dict__.items()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_from_to_date():
|
|
||||||
import osxphotos
|
|
||||||
import datetime as dt
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
os.environ["TZ"] = "US/Pacific"
|
|
||||||
time.tzset()
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
|
|
||||||
photos = photosdb.photos(from_date=dt.datetime(2018, 10, 28))
|
|
||||||
assert len(photos) == 6
|
|
||||||
|
|
||||||
photos = photosdb.photos(to_date=dt.datetime(2018, 10, 28))
|
|
||||||
assert len(photos) == 6
|
|
||||||
|
|
||||||
photos = photosdb.photos(
|
|
||||||
from_date=dt.datetime(2018, 9, 28), to_date=dt.datetime(2018, 9, 29)
|
|
||||||
)
|
|
||||||
assert len(photos) == 4
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -57,6 +57,7 @@ ALBUMS = [
|
|||||||
"EmptyAlbum",
|
"EmptyAlbum",
|
||||||
"2018-10 - Sponsion, Museum, Frühstück, Römermuseum",
|
"2018-10 - Sponsion, Museum, Frühstück, Römermuseum",
|
||||||
"2019-10/11 Paris Clermont",
|
"2019-10/11 Paris Clermont",
|
||||||
|
"Multi Keyword",
|
||||||
]
|
]
|
||||||
KEYWORDS_DICT = {
|
KEYWORDS_DICT = {
|
||||||
"Kids": 4,
|
"Kids": 4,
|
||||||
@ -86,6 +87,7 @@ ALBUM_DICT = {
|
|||||||
"EmptyAlbum": 0,
|
"EmptyAlbum": 0,
|
||||||
"2018-10 - Sponsion, Museum, Frühstück, Römermuseum": 1,
|
"2018-10 - Sponsion, Museum, Frühstück, Römermuseum": 1,
|
||||||
"2019-10/11 Paris Clermont": 1,
|
"2019-10/11 Paris Clermont": 1,
|
||||||
|
"Multi Keyword": 2,
|
||||||
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
||||||
|
|
||||||
UUID_DICT = {
|
UUID_DICT = {
|
||||||
@ -112,17 +114,19 @@ UUID_DICT = {
|
|||||||
"movie": "D1359D09-1373-4F3B-B0E3-1A4DE573E4A3",
|
"movie": "D1359D09-1373-4F3B-B0E3-1A4DE573E4A3",
|
||||||
"description_newlines": "7F74DD34-5920-4DA3-B284-479887A34F66",
|
"description_newlines": "7F74DD34-5920-4DA3-B284-479887A34F66",
|
||||||
"no_duplicates": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
"no_duplicates": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||||
|
"multi_query_1": "D79B8D77-BFFC-460B-9312-034F2877D35B",
|
||||||
|
"multi_query_2": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID_DICT_LOCAL = {
|
UUID_DICT_LOCAL = {
|
||||||
"not_visible": "4A836160-51B2-4E32-907D-ECDDB2CEC657", # IMG_9815.JPG
|
"not_visible": "4A836160-51B2-4E32-907D-ECDDB2CEC657", # IMG_9815.JPG
|
||||||
"burst": "9A5B4CE6-6A9F-4917-95D4-1C98D14FCE4F", # IMG_9812.JPG
|
"burst": "9A5B4CE6-6A9F-4917-95D4-1C98D14FCE4F", # IMG_9812.JPG
|
||||||
"burst_key": "9A5B4CE6-6A9F-4917-95D4-1C98D14FCE4F", # IMG_9812.JPG
|
"burst_key": "9A5B4CE6-6A9F-4917-95D4-1C98D14FCE4F", # IMG_9812.JPG
|
||||||
"burst_not_key": "4A836160-51B2-4E32-907D-ECDDB2CEC657", # IMG_9815.JPG
|
"burst_not_key": "4A836160-51B2-4E32-907D-ECDDB2CEC657", # IMG_9815.JPG
|
||||||
"burst_selected": "75154738-83AA-4DCD-A913-632D5D1C0FEE", # IMG_9814.JPG
|
"burst_selected": "75154738-83AA-4DCD-A913-632D5D1C0FEE", # IMG_9814.JPG
|
||||||
"burst_not_selected": "89E235DD-B9AC-4E8D-BDA2-986981CA7582", # IMG_9813.JPG
|
"burst_not_selected": "89E235DD-B9AC-4E8D-BDA2-986981CA7582", # IMG_9813.JPG
|
||||||
"burst_default": "F5E6BD24-B493-44E9-BDA2-7AD9D2CC8C9D", # IMG_9816.JPG
|
"burst_default": "F5E6BD24-B493-44E9-BDA2-7AD9D2CC8C9D", # IMG_9816.JPG
|
||||||
"burst_not_default": "75154738-83AA-4DCD-A913-632D5D1C0FEE", # IMG_9814.JPG
|
"burst_not_default": "75154738-83AA-4DCD-A913-632D5D1C0FEE", # IMG_9814.JPG
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID_PUMPKIN_FARM = [
|
UUID_PUMPKIN_FARM = [
|
||||||
@ -374,7 +378,7 @@ def test_attributes(photosdb):
|
|||||||
)
|
)
|
||||||
assert p.description == "Girl holding pumpkin"
|
assert p.description == "Girl holding pumpkin"
|
||||||
assert p.title == "I found one!"
|
assert p.title == "I found one!"
|
||||||
assert sorted(p.albums) == ["Pumpkin Farm", "Test Album"]
|
assert sorted(p.albums) == ["Multi Keyword", "Pumpkin Farm", "Test Album"]
|
||||||
assert p.persons == ["Katie"]
|
assert p.persons == ["Katie"]
|
||||||
assert p.path.endswith(
|
assert p.path.endswith(
|
||||||
"tests/Test-10.15.7.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
"tests/Test-10.15.7.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
||||||
@ -383,7 +387,7 @@ def test_attributes(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_attributes_2(photosdb):
|
def test_attributes_2(photosdb):
|
||||||
""" Test attributes including height, width, etc """
|
"""Test attributes including height, width, etc"""
|
||||||
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
||||||
assert len(photos) == 1
|
assert len(photos) == 1
|
||||||
@ -403,7 +407,11 @@ def test_attributes_2(photosdb):
|
|||||||
)
|
)
|
||||||
assert p.description == "Bride Wedding day"
|
assert p.description == "Bride Wedding day"
|
||||||
assert p.title is None
|
assert p.title is None
|
||||||
assert sorted(p.albums) == ["AlbumInFolder", "I have a deleted twin"]
|
assert sorted(p.albums) == [
|
||||||
|
"AlbumInFolder",
|
||||||
|
"I have a deleted twin",
|
||||||
|
"Multi Keyword",
|
||||||
|
]
|
||||||
assert p.persons == ["Maria"]
|
assert p.persons == ["Maria"]
|
||||||
assert p.path.endswith(
|
assert p.path.endswith(
|
||||||
"tests/Test-10.15.7.photoslibrary/originals/E/E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51.jpeg"
|
"tests/Test-10.15.7.photoslibrary/originals/E/E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51.jpeg"
|
||||||
@ -461,7 +469,7 @@ def test_not_hidden(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_visible(photosdb):
|
def test_visible(photosdb):
|
||||||
""" test visible """
|
"""test visible"""
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
|
||||||
assert len(photos) == 1
|
assert len(photos) == 1
|
||||||
p = photos[0]
|
p = photos[0]
|
||||||
@ -469,7 +477,7 @@ def test_visible(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_not_burst(photosdb):
|
def test_not_burst(photosdb):
|
||||||
""" test not burst """
|
"""test not burst"""
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
|
||||||
assert len(photos) == 1
|
assert len(photos) == 1
|
||||||
p = photos[0]
|
p = photos[0]
|
||||||
@ -597,14 +605,14 @@ def test_count(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_photos_intrash_1(photosdb):
|
def test_photos_intrash_1(photosdb):
|
||||||
""" test PhotosDB.photos(intrash=True) """
|
"""test PhotosDB.photos(intrash=True)"""
|
||||||
|
|
||||||
photos = photosdb.photos(intrash=True)
|
photos = photosdb.photos(intrash=True)
|
||||||
assert len(photos) == PHOTOS_IN_TRASH_LEN
|
assert len(photos) == PHOTOS_IN_TRASH_LEN
|
||||||
|
|
||||||
|
|
||||||
def test_photos_intrash_2(photosdb):
|
def test_photos_intrash_2(photosdb):
|
||||||
""" test PhotosDB.photos(intrash=True) """
|
"""test PhotosDB.photos(intrash=True)"""
|
||||||
|
|
||||||
photos = photosdb.photos(intrash=True)
|
photos = photosdb.photos(intrash=True)
|
||||||
for p in photos:
|
for p in photos:
|
||||||
@ -612,7 +620,7 @@ def test_photos_intrash_2(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_photos_intrash_3(photosdb):
|
def test_photos_intrash_3(photosdb):
|
||||||
""" test PhotosDB.photos(intrash=False) """
|
"""test PhotosDB.photos(intrash=False)"""
|
||||||
|
|
||||||
photos = photosdb.photos(intrash=False)
|
photos = photosdb.photos(intrash=False)
|
||||||
for p in photos:
|
for p in photos:
|
||||||
@ -620,7 +628,7 @@ def test_photos_intrash_3(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_photoinfo_intrash_1(photosdb):
|
def test_photoinfo_intrash_1(photosdb):
|
||||||
""" Test PhotoInfo.intrash """
|
"""Test PhotoInfo.intrash"""
|
||||||
|
|
||||||
p = photosdb.photos(uuid=[UUID_DICT["intrash"]], intrash=True)[0]
|
p = photosdb.photos(uuid=[UUID_DICT["intrash"]], intrash=True)[0]
|
||||||
assert p.intrash
|
assert p.intrash
|
||||||
@ -628,14 +636,14 @@ def test_photoinfo_intrash_1(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_photoinfo_intrash_2(photosdb):
|
def test_photoinfo_intrash_2(photosdb):
|
||||||
""" Test PhotoInfo.intrash and intrash=default"""
|
"""Test PhotoInfo.intrash and intrash=default"""
|
||||||
|
|
||||||
p = photosdb.photos(uuid=[UUID_DICT["intrash"]])
|
p = photosdb.photos(uuid=[UUID_DICT["intrash"]])
|
||||||
assert not p
|
assert not p
|
||||||
|
|
||||||
|
|
||||||
def test_photoinfo_intrash_3(photosdb):
|
def test_photoinfo_intrash_3(photosdb):
|
||||||
""" Test PhotoInfo.intrash and photo has keyword and person """
|
"""Test PhotoInfo.intrash and photo has keyword and person"""
|
||||||
|
|
||||||
p = photosdb.photos(uuid=[UUID_DICT["intrash_person_keywords"]], intrash=True)[0]
|
p = photosdb.photos(uuid=[UUID_DICT["intrash_person_keywords"]], intrash=True)[0]
|
||||||
assert p.intrash
|
assert p.intrash
|
||||||
@ -644,7 +652,7 @@ def test_photoinfo_intrash_3(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_photoinfo_intrash_4(photosdb):
|
def test_photoinfo_intrash_4(photosdb):
|
||||||
""" Test PhotoInfo.intrash and photo has keyword and person """
|
"""Test PhotoInfo.intrash and photo has keyword and person"""
|
||||||
|
|
||||||
p = photosdb.photos(persons=["Maria"], intrash=True)[0]
|
p = photosdb.photos(persons=["Maria"], intrash=True)[0]
|
||||||
assert p.intrash
|
assert p.intrash
|
||||||
@ -653,7 +661,7 @@ def test_photoinfo_intrash_4(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_photoinfo_intrash_5(photosdb):
|
def test_photoinfo_intrash_5(photosdb):
|
||||||
""" Test PhotoInfo.intrash and photo has keyword and person """
|
"""Test PhotoInfo.intrash and photo has keyword and person"""
|
||||||
|
|
||||||
p = photosdb.photos(keywords=["wedding"], intrash=True)[0]
|
p = photosdb.photos(keywords=["wedding"], intrash=True)[0]
|
||||||
assert p.intrash
|
assert p.intrash
|
||||||
@ -662,7 +670,7 @@ def test_photoinfo_intrash_5(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_photoinfo_not_intrash(photosdb):
|
def test_photoinfo_not_intrash(photosdb):
|
||||||
""" Test PhotoInfo.intrash """
|
"""Test PhotoInfo.intrash"""
|
||||||
|
|
||||||
p = photosdb.photos(uuid=[UUID_DICT["not_intrash"]])[0]
|
p = photosdb.photos(uuid=[UUID_DICT["not_intrash"]])[0]
|
||||||
assert not p.intrash
|
assert not p.intrash
|
||||||
@ -686,7 +694,7 @@ def test_keyword_not_in_album(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_album_folder_name(photosdb):
|
def test_album_folder_name(photosdb):
|
||||||
"""Test query with album name same as a folder name """
|
"""Test query with album name same as a folder name"""
|
||||||
|
|
||||||
photos = photosdb.photos(albums=["Pumpkin Farm"])
|
photos = photosdb.photos(albums=["Pumpkin Farm"])
|
||||||
assert sorted(p.uuid for p in photos) == sorted(UUID_PUMPKIN_FARM)
|
assert sorted(p.uuid for p in photos) == sorted(UUID_PUMPKIN_FARM)
|
||||||
@ -712,7 +720,7 @@ def test_get_library_path(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_db_connection(photosdb):
|
def test_get_db_connection(photosdb):
|
||||||
""" Test PhotosDB.get_db_connection """
|
"""Test PhotosDB.get_db_connection"""
|
||||||
|
|
||||||
conn, cursor = photosdb.get_db_connection()
|
conn, cursor = photosdb.get_db_connection()
|
||||||
|
|
||||||
@ -995,7 +1003,7 @@ def test_export_no_original_filename(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_eq():
|
def test_eq():
|
||||||
""" Test equality of two PhotoInfo objects """
|
"""Test equality of two PhotoInfo objects"""
|
||||||
|
|
||||||
photosdb1 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
photosdb1 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
@ -1005,7 +1013,7 @@ def test_eq():
|
|||||||
|
|
||||||
|
|
||||||
def test_eq_2():
|
def test_eq_2():
|
||||||
""" Test equality of two PhotoInfo objects when one has memoized property """
|
"""Test equality of two PhotoInfo objects when one has memoized property"""
|
||||||
|
|
||||||
photosdb1 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
photosdb1 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
@ -1049,7 +1057,7 @@ def test_photosinfo_repr(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_from_to_date(photosdb):
|
def test_from_to_date(photosdb):
|
||||||
""" test from_date / to_date """
|
"""test from_date / to_date"""
|
||||||
|
|
||||||
os.environ["TZ"] = "US/Pacific"
|
os.environ["TZ"] = "US/Pacific"
|
||||||
time.tzset()
|
time.tzset()
|
||||||
@ -1067,7 +1075,7 @@ def test_from_to_date(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_from_to_date_tz(photosdb):
|
def test_from_to_date_tz(photosdb):
|
||||||
""" Test from_date / to_date with and without timezone """
|
"""Test from_date / to_date with and without timezone"""
|
||||||
|
|
||||||
os.environ["TZ"] = "US/Pacific"
|
os.environ["TZ"] = "US/Pacific"
|
||||||
time.tzset()
|
time.tzset()
|
||||||
@ -1104,7 +1112,7 @@ def test_from_to_date_tz(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_date_invalid():
|
def test_date_invalid():
|
||||||
""" Test date is invalid """
|
"""Test date is invalid"""
|
||||||
# doesn't run correctly with the module-level fixture
|
# doesn't run correctly with the module-level fixture
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
import osxphotos
|
import osxphotos
|
||||||
@ -1119,7 +1127,7 @@ def test_date_invalid():
|
|||||||
|
|
||||||
|
|
||||||
def test_date_modified_invalid(photosdb):
|
def test_date_modified_invalid(photosdb):
|
||||||
""" Test date modified is invalid """
|
"""Test date modified is invalid"""
|
||||||
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["date_invalid"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["date_invalid"]])
|
||||||
assert len(photos) == 1
|
assert len(photos) == 1
|
||||||
@ -1128,14 +1136,14 @@ def test_date_modified_invalid(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_import_session_count(photosdb):
|
def test_import_session_count(photosdb):
|
||||||
""" Test PhotosDB.import_session """
|
"""Test PhotosDB.import_session"""
|
||||||
|
|
||||||
import_sessions = photosdb.import_info
|
import_sessions = photosdb.import_info
|
||||||
assert len(import_sessions) == PHOTOS_DB_IMPORT_SESSIONS
|
assert len(import_sessions) == PHOTOS_DB_IMPORT_SESSIONS
|
||||||
|
|
||||||
|
|
||||||
def test_import_session_photo(photosdb):
|
def test_import_session_photo(photosdb):
|
||||||
""" Test photo.import_session """
|
"""Test photo.import_session"""
|
||||||
|
|
||||||
photo = photosdb.get_photo(UUID_DICT["import_session"])
|
photo = photosdb.get_photo(UUID_DICT["import_session"])
|
||||||
import_session = photo.import_info
|
import_session = photo.import_info
|
||||||
@ -1173,7 +1181,7 @@ def test_import_session_photo(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_uti(photosdb):
|
def test_uti(photosdb):
|
||||||
""" test uti """
|
"""test uti"""
|
||||||
|
|
||||||
for uuid, uti in UTI_DICT.items():
|
for uuid, uti in UTI_DICT.items():
|
||||||
photo = photosdb.get_photo(uuid)
|
photo = photosdb.get_photo(uuid)
|
||||||
@ -1182,7 +1190,7 @@ def test_uti(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_raw(photosdb):
|
def test_raw(photosdb):
|
||||||
""" Test various raw properties """
|
"""Test various raw properties"""
|
||||||
|
|
||||||
for uuid, rawinfo in RAW_DICT.items():
|
for uuid, rawinfo in RAW_DICT.items():
|
||||||
photo = photosdb.get_photo(uuid)
|
photo = photosdb.get_photo(uuid)
|
||||||
@ -1195,7 +1203,7 @@ def test_raw(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_verbose(capsys):
|
def test_verbose(capsys):
|
||||||
""" test verbose output in PhotosDB() """
|
"""test verbose output in PhotosDB()"""
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB, verbose=print)
|
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB, verbose=print)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
@ -1203,7 +1211,7 @@ def test_verbose(capsys):
|
|||||||
|
|
||||||
|
|
||||||
def test_original_filename(photosdb):
|
def test_original_filename(photosdb):
|
||||||
""" test original filename """
|
"""test original filename"""
|
||||||
uuid = ORIGINAL_FILENAME_DICT["uuid"]
|
uuid = ORIGINAL_FILENAME_DICT["uuid"]
|
||||||
photo = photosdb.get_photo(uuid)
|
photo = photosdb.get_photo(uuid)
|
||||||
assert photo.original_filename == ORIGINAL_FILENAME_DICT["original_filename"]
|
assert photo.original_filename == ORIGINAL_FILENAME_DICT["original_filename"]
|
||||||
@ -1220,7 +1228,7 @@ def test_original_filename(photosdb):
|
|||||||
# They test things difficult to test in the test libraries
|
# They test things difficult to test in the test libraries
|
||||||
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
||||||
def test_not_visible_burst(photosdb_local):
|
def test_not_visible_burst(photosdb_local):
|
||||||
""" test not visible and burst (needs image from local library) """
|
"""test not visible and burst (needs image from local library)"""
|
||||||
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["not_visible"])
|
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["not_visible"])
|
||||||
assert not photo.visible
|
assert not photo.visible
|
||||||
assert photo.burst
|
assert photo.burst
|
||||||
@ -1228,7 +1236,7 @@ def test_not_visible_burst(photosdb_local):
|
|||||||
|
|
||||||
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
||||||
def test_visible_burst(photosdb_local):
|
def test_visible_burst(photosdb_local):
|
||||||
""" test not visible and burst (needs image from local library) """
|
"""test not visible and burst (needs image from local library)"""
|
||||||
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["burst"])
|
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["burst"])
|
||||||
assert photo.visible
|
assert photo.visible
|
||||||
assert photo.burst
|
assert photo.burst
|
||||||
@ -1237,7 +1245,7 @@ def test_visible_burst(photosdb_local):
|
|||||||
|
|
||||||
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
||||||
def test_burst_key(photosdb_local):
|
def test_burst_key(photosdb_local):
|
||||||
""" test burst_key """
|
"""test burst_key"""
|
||||||
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["burst_key"])
|
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["burst_key"])
|
||||||
assert photo.burst_key
|
assert photo.burst_key
|
||||||
|
|
||||||
@ -1247,7 +1255,7 @@ def test_burst_key(photosdb_local):
|
|||||||
|
|
||||||
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
||||||
def test_burst_selected(photosdb_local):
|
def test_burst_selected(photosdb_local):
|
||||||
""" test burst_selected """
|
"""test burst_selected"""
|
||||||
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["burst_selected"])
|
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["burst_selected"])
|
||||||
assert photo.burst_selected
|
assert photo.burst_selected
|
||||||
|
|
||||||
@ -1257,7 +1265,7 @@ def test_burst_selected(photosdb_local):
|
|||||||
|
|
||||||
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
|
||||||
def test_burst_default_pic(photosdb_local):
|
def test_burst_default_pic(photosdb_local):
|
||||||
""" test burst_default_pick"""
|
"""test burst_default_pick"""
|
||||||
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["burst_default"])
|
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["burst_default"])
|
||||||
assert photo.burst_default_pick
|
assert photo.burst_default_pick
|
||||||
|
|
||||||
@ -1266,7 +1274,7 @@ def test_burst_default_pic(photosdb_local):
|
|||||||
|
|
||||||
|
|
||||||
def test_is_reference(photosdb):
|
def test_is_reference(photosdb):
|
||||||
""" test isreference """
|
"""test isreference"""
|
||||||
|
|
||||||
photo = photosdb.get_photo(UUID_IS_REFERENCE)
|
photo = photosdb.get_photo(UUID_IS_REFERENCE)
|
||||||
assert photo.isreference
|
assert photo.isreference
|
||||||
@ -1275,7 +1283,7 @@ def test_is_reference(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_adjustments(photosdb):
|
def test_adjustments(photosdb):
|
||||||
""" test adjustments/AdjustmentsInfo """
|
"""test adjustments/AdjustmentsInfo"""
|
||||||
from osxphotos.adjustmentsinfo import AdjustmentsInfo
|
from osxphotos.adjustmentsinfo import AdjustmentsInfo
|
||||||
|
|
||||||
photo = photosdb.get_photo(UUID_DICT["adjustments_info"])
|
photo = photosdb.get_photo(UUID_DICT["adjustments_info"])
|
||||||
@ -1337,14 +1345,14 @@ def test_adjustments(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_no_adjustments(photosdb):
|
def test_no_adjustments(photosdb):
|
||||||
""" test adjustments when photo has no adjusments"""
|
"""test adjustments when photo has no adjusments"""
|
||||||
|
|
||||||
photo = photosdb.get_photo(UUID_DICT["no_adjustments"])
|
photo = photosdb.get_photo(UUID_DICT["no_adjustments"])
|
||||||
assert photo.adjustments is None
|
assert photo.adjustments is None
|
||||||
|
|
||||||
|
|
||||||
def test_exiftool_newlines_in_description(photosdb):
|
def test_exiftool_newlines_in_description(photosdb):
|
||||||
""" Test that exiftool code removes newlines embedded in description, issue #393"""
|
"""Test that exiftool code removes newlines embedded in description, issue #393"""
|
||||||
|
|
||||||
photo = photosdb.get_photo(UUID_DICT["description_newlines"])
|
photo = photosdb.get_photo(UUID_DICT["description_newlines"])
|
||||||
exif = photo._exiftool_dict()
|
exif = photo._exiftool_dict()
|
||||||
@ -1366,3 +1374,33 @@ def test_duplicates_2(photosdb):
|
|||||||
|
|
||||||
photo = photosdb.get_photo(uuid=UUID_DICT["no_duplicates"])
|
photo = photosdb.get_photo(uuid=UUID_DICT["no_duplicates"])
|
||||||
assert not photo.duplicates
|
assert not photo.duplicates
|
||||||
|
|
||||||
|
|
||||||
|
def test_compound_query(photosdb):
|
||||||
|
""" test photos() with multiple query terms """
|
||||||
|
photos = photosdb.photos(persons=["Katie", "Maria"], albums=["Multi Keyword"])
|
||||||
|
|
||||||
|
assert len(photos) == 2
|
||||||
|
assert UUID_DICT["multi_query_1"] in [p.uuid for p in photos]
|
||||||
|
assert UUID_DICT["multi_query_2"] in [p.uuid for p in photos]
|
||||||
|
|
||||||
|
|
||||||
|
def test_multi_keyword(photosdb):
|
||||||
|
""" test photos() with multiple keywords """
|
||||||
|
photos = photosdb.photos(keywords=["Kids", "wedding"])
|
||||||
|
|
||||||
|
assert len(photos) == 6
|
||||||
|
|
||||||
|
|
||||||
|
def test_multi_album(photosdb):
|
||||||
|
""" test photos() with multiple albums """
|
||||||
|
photos = photosdb.photos(albums=["Pumpkin Farm", "Test Album"])
|
||||||
|
|
||||||
|
assert len(photos) == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_multi_uuid(photosdb):
|
||||||
|
""" test photos() with multiple uuids """
|
||||||
|
photos = photosdb.photos(uuid=[UUID_DICT["favorite"], UUID_DICT["not_favorite"]])
|
||||||
|
|
||||||
|
assert len(photos) == 2
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
from osxphotos._constants import _UNKNOWN_PERSON
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.13.6.photoslibrary/database/photos.db"
|
PHOTOS_DB = "./tests/Test-10.13.6.photoslibrary/database/photos.db"
|
||||||
@ -31,80 +33,62 @@ PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1, _UNKNOWN_PERSON: 1}
|
|||||||
ALBUM_DICT = {"Pumpkin Farm": 3, "TestAlbum": 1, "AlbumInFolder": 1}
|
ALBUM_DICT = {"Pumpkin Farm": 3, "TestAlbum": 1, "AlbumInFolder": 1}
|
||||||
|
|
||||||
|
|
||||||
def test_init():
|
@pytest.fixture(scope="module")
|
||||||
import osxphotos
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
def test_init(photosdb):
|
||||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
assert isinstance(photosdb, osxphotos.PhotosDB)
|
||||||
|
|
||||||
|
|
||||||
def test_db_version():
|
def test_db_version(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert photosdb.db_version == "3301"
|
assert photosdb.db_version == "3301"
|
||||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
def test_persons(photosdb):
|
||||||
import osxphotos
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "Katie" in photosdb.persons
|
assert "Katie" in photosdb.persons
|
||||||
assert collections.Counter(PERSONS) == collections.Counter(photosdb.persons)
|
assert collections.Counter(PERSONS) == collections.Counter(photosdb.persons)
|
||||||
|
|
||||||
|
|
||||||
def test_keywords():
|
def test_keywords(photosdb):
|
||||||
import osxphotos
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "wedding" in photosdb.keywords
|
assert "wedding" in photosdb.keywords
|
||||||
assert collections.Counter(KEYWORDS) == collections.Counter(photosdb.keywords)
|
assert collections.Counter(KEYWORDS) == collections.Counter(photosdb.keywords)
|
||||||
|
|
||||||
|
|
||||||
def test_album_names():
|
def test_album_names(photosdb):
|
||||||
import osxphotos
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "Pumpkin Farm" in photosdb.albums
|
assert "Pumpkin Farm" in photosdb.albums
|
||||||
assert collections.Counter(ALBUMS) == collections.Counter(photosdb.albums)
|
assert collections.Counter(ALBUMS) == collections.Counter(photosdb.albums)
|
||||||
|
|
||||||
|
|
||||||
def test_keywords_dict():
|
def test_keywords_dict(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
keywords = photosdb.keywords_as_dict
|
keywords = photosdb.keywords_as_dict
|
||||||
assert keywords["wedding"] == 2
|
assert keywords["wedding"] == 2
|
||||||
assert keywords == KEYWORDS_DICT
|
assert keywords == KEYWORDS_DICT
|
||||||
|
|
||||||
|
|
||||||
def test_persons_as_dict():
|
def test_persons_as_dict(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
persons = photosdb.persons_as_dict
|
persons = photosdb.persons_as_dict
|
||||||
assert persons["Maria"] == 1
|
assert persons["Maria"] == 1
|
||||||
assert persons == PERSONS_DICT
|
assert persons == PERSONS_DICT
|
||||||
|
|
||||||
|
|
||||||
def test_albums_as_dict():
|
def test_albums_as_dict(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
albums = photosdb.albums_as_dict
|
albums = photosdb.albums_as_dict
|
||||||
assert albums["Pumpkin Farm"] == 3
|
assert albums["Pumpkin Farm"] == 3
|
||||||
assert albums == ALBUM_DICT
|
assert albums == ALBUM_DICT
|
||||||
|
|
||||||
|
|
||||||
def test_attributes():
|
def test_attributes(photosdb):
|
||||||
import datetime
|
import datetime
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["RWmFYiDjSyKjeK8Pfna0Eg"])
|
photos = photosdb.photos(uuid=["RWmFYiDjSyKjeK8Pfna0Eg"])
|
||||||
assert len(photos) == 1
|
assert len(photos) == 1
|
||||||
p = photos[0]
|
p = photos[0]
|
||||||
@ -124,38 +108,25 @@ def test_attributes():
|
|||||||
assert p.ismissing == False
|
assert p.ismissing == False
|
||||||
|
|
||||||
|
|
||||||
def test_missing():
|
def test_missing(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["6iAZJP7ZQ5iXxapoJb3ytA"])
|
photos = photosdb.photos(uuid=["6iAZJP7ZQ5iXxapoJb3ytA"])
|
||||||
assert len(photos) == 1
|
assert len(photos) == 1
|
||||||
p = photos[0]
|
p = photos[0]
|
||||||
assert p.path == None
|
assert p.path is None
|
||||||
assert p.ismissing == True
|
assert p.ismissing
|
||||||
|
|
||||||
|
|
||||||
def test_count():
|
def test_count(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos()
|
photos = photosdb.photos()
|
||||||
assert len(photos) == 7
|
assert len(photos) == 7
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_2():
|
def test_keyword_2(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(keywords=["wedding"])
|
photos = photosdb.photos(keywords=["wedding"])
|
||||||
assert len(photos) == 2
|
assert len(photos) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_not_in_album():
|
def test_keyword_not_in_album(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
|
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
|
||||||
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
|
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
|
||||||
photos2 = photosdb.photos(keywords=["Kids"])
|
photos2 = photosdb.photos(keywords=["Kids"])
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
PHOTOS_DB_CLOUD = "./tests/Test-Cloud-10.15.1.photoslibrary/database/photos.db"
|
PHOTOS_DB_CLOUD = "./tests/Test-Cloud-10.15.1.photoslibrary/database/photos.db"
|
||||||
PHOTOS_DB_NOT_CLOUD = "./tests/Test-10.15.1.photoslibrary/database/photos.db"
|
PHOTOS_DB_NOT_CLOUD = "./tests/Test-10.15.1.photoslibrary/database/photos.db"
|
||||||
|
|
||||||
@ -13,37 +15,30 @@ UUID_DICT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_incloud():
|
@pytest.fixture(scope="module")
|
||||||
import osxphotos
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB_CLOUD)
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
|
def test_incloud(photosdb):
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["incloud"]])
|
||||||
|
|
||||||
assert photos[0].incloud
|
assert photos[0].incloud
|
||||||
|
|
||||||
|
|
||||||
def test_not_incloud():
|
def test_not_incloud(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
||||||
|
|
||||||
assert not photos[0].incloud
|
assert not photos[0].incloud
|
||||||
|
|
||||||
|
|
||||||
def test_cloudasset_1():
|
def test_cloudasset_1(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
|
||||||
|
|
||||||
assert photos[0].iscloudasset
|
assert photos[0].iscloudasset
|
||||||
|
|
||||||
|
|
||||||
def test_cloudasset_2():
|
def test_cloudasset_2(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
||||||
|
|
||||||
# not_incloud is still a cloud asset
|
# not_incloud is still a cloud asset
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
PHOTOS_DB_CLOUD = "./tests/Test-Cloud-10.15.6.photoslibrary/database/photos.db"
|
PHOTOS_DB_CLOUD = "./tests/Test-Cloud-10.15.6.photoslibrary/database/photos.db"
|
||||||
PHOTOS_DB_NOT_CLOUD = "./tests/Test-10.15.6.photoslibrary/database/photos.db"
|
PHOTOS_DB_NOT_CLOUD = "./tests/Test-10.15.6.photoslibrary/database/photos.db"
|
||||||
|
|
||||||
@ -13,37 +15,30 @@ UUID_DICT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_incloud():
|
@pytest.fixture(scope="module")
|
||||||
import osxphotos
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB_CLOUD)
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
|
def test_incloud(photosdb):
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["incloud"]])
|
||||||
|
|
||||||
assert photos[0].incloud
|
assert photos[0].incloud
|
||||||
|
|
||||||
|
|
||||||
def test_not_incloud():
|
def test_not_incloud(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
||||||
|
|
||||||
assert not photos[0].incloud
|
assert not photos[0].incloud
|
||||||
|
|
||||||
|
|
||||||
def test_cloudasset_1():
|
def test_cloudasset_1(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
|
||||||
|
|
||||||
assert photos[0].iscloudasset
|
assert photos[0].iscloudasset
|
||||||
|
|
||||||
|
|
||||||
def test_cloudasset_2():
|
def test_cloudasset_2(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
||||||
|
|
||||||
# not_incloud is still a cloud asset
|
# not_incloud is still a cloud asset
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
PHOTOS_DB_CLOUD = "./tests/Test-Cloud-10.14.6.photoslibrary/database/photos.db"
|
PHOTOS_DB_CLOUD = "./tests/Test-Cloud-10.14.6.photoslibrary/database/photos.db"
|
||||||
PHOTOS_DB_NOT_CLOUD = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
|
PHOTOS_DB_NOT_CLOUD = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
|
||||||
|
|
||||||
@ -13,37 +15,29 @@ UUID_DICT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_incloud():
|
@pytest.fixture(scope="module")
|
||||||
import osxphotos
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB_CLOUD)
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
def test_incloud(photosdb):
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["incloud"]])
|
||||||
|
|
||||||
assert photos[0].incloud
|
assert photos[0].incloud
|
||||||
|
|
||||||
|
|
||||||
def test_not_incloud():
|
def test_not_incloud(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
||||||
|
|
||||||
assert not photos[0].incloud
|
assert not photos[0].incloud
|
||||||
|
|
||||||
|
|
||||||
def test_cloudasset_1():
|
def test_cloudasset_1(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
|
||||||
|
|
||||||
assert photos[0].iscloudasset
|
assert photos[0].iscloudasset
|
||||||
|
|
||||||
|
|
||||||
def test_cloudasset_2():
|
def test_cloudasset_2(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
||||||
|
|
||||||
# not_incloud is still a cloud asset
|
# not_incloud is still a cloud asset
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-Cloud-10.15.1.photoslibrary/database/photos.db"
|
PHOTOS_DB = "./tests/Test-Cloud-10.15.1.photoslibrary/database/photos.db"
|
||||||
|
|
||||||
UUID_DICT = {
|
UUID_DICT = {
|
||||||
@ -10,38 +12,34 @@ UUID_DICT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_live_photo():
|
@pytest.fixture(scope="module")
|
||||||
import osxphotos
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
|
def test_live_photo(photosdb):
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
|
||||||
|
|
||||||
assert photos[0].live_photo
|
assert photos[0].live_photo
|
||||||
assert photos[0].path_live_photo is not None
|
assert photos[0].path_live_photo is not None
|
||||||
|
|
||||||
|
|
||||||
def test_not_live_photo():
|
def test_not_live_photo(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_live"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_live"]])
|
||||||
|
|
||||||
assert not photos[0].live_photo
|
assert not photos[0].live_photo
|
||||||
assert photos[0].path_live_photo is None
|
assert photos[0].path_live_photo is None
|
||||||
|
|
||||||
|
|
||||||
def test_export_live_1():
|
def test_export_live_1(photosdb):
|
||||||
# export a live photo and associated .mov
|
# export a live photo and associated .mov
|
||||||
import glob
|
import glob
|
||||||
import os.path
|
import os.path
|
||||||
import pathlib
|
import pathlib
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
filename = photos[0].original_filename
|
||||||
@ -56,18 +54,15 @@ def test_export_live_1():
|
|||||||
assert got_movie in files
|
assert got_movie in files
|
||||||
|
|
||||||
|
|
||||||
def test_export_live_2():
|
def test_export_live_2(photosdb):
|
||||||
# don't export the live photo
|
# don't export the live photo
|
||||||
import glob
|
import glob
|
||||||
import os.path
|
import os.path
|
||||||
import pathlib
|
import pathlib
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
filename = photos[0].original_filename
|
||||||
@ -82,7 +77,7 @@ def test_export_live_2():
|
|||||||
assert got_movie not in files
|
assert got_movie not in files
|
||||||
|
|
||||||
|
|
||||||
def test_export_live_3():
|
def test_export_live_3(photosdb):
|
||||||
# export a live photo and associated .mov,
|
# export a live photo and associated .mov,
|
||||||
# check list return of export
|
# check list return of export
|
||||||
import glob
|
import glob
|
||||||
@ -90,11 +85,8 @@ def test_export_live_3():
|
|||||||
import pathlib
|
import pathlib
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
|
||||||
|
|
||||||
filename = photos[0].original_filename
|
filename = photos[0].original_filename
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.15.7.photoslibrary/database/photos.db"
|
PHOTOS_DB = "./tests/Test-10.15.7.photoslibrary/database/photos.db"
|
||||||
|
|
||||||
UUID_DICT = {
|
UUID_DICT = {
|
||||||
@ -7,12 +9,13 @@ UUID_DICT = {
|
|||||||
"not_modified": "D05A5FE3-15FB-49A1-A15D-AB3DA6F8B068",
|
"not_modified": "D05A5FE3-15FB-49A1-A15D-AB3DA6F8B068",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
|
||||||
def test_modified():
|
def test_modified(photosdb):
|
||||||
import datetime
|
import datetime
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["modified"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["modified"]])
|
||||||
assert photos[0].date_modified is not None
|
assert photos[0].date_modified is not None
|
||||||
assert photos[0].date_modified == datetime.datetime(
|
assert photos[0].date_modified == datetime.datetime(
|
||||||
@ -27,9 +30,6 @@ def test_modified():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_not_modified():
|
def test_not_modified(photosdb):
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["not_modified"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["not_modified"]])
|
||||||
assert photos[0].date_modified is None
|
assert photos[0].date_modified is None
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
|
PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
|
||||||
PHOTOS_DB_PATH = "/Test-10.14.6.photoslibrary/database/photos.db"
|
PHOTOS_DB_PATH = "/Test-10.14.6.photoslibrary/database/photos.db"
|
||||||
PHOTOS_LIBRARY_PATH = "/Test-Shared-10.14.6.photoslibrary"
|
PHOTOS_LIBRARY_PATH = "/Test-Shared-10.14.6.photoslibrary"
|
||||||
@ -9,12 +11,12 @@ UUID_DICT = {
|
|||||||
"not_modified": "35243F7D-88C4-4408-B516-C74406E90C15",
|
"not_modified": "35243F7D-88C4-4408-B516-C74406E90C15",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def photosdb():
|
||||||
|
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
|
||||||
def test_modified():
|
|
||||||
import datetime
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
def test_modified(photosdb):
|
||||||
photos = photosdb.photos(uuid=[UUID_DICT["modified"]])
|
photos = photosdb.photos(uuid=[UUID_DICT["modified"]])
|
||||||
assert photos[0].date_modified is not None
|
assert photos[0].date_modified is not None
|
||||||
assert photos[0].date_modified.isoformat() == "2019-12-01T11:43:45.714123-04:00"
|
assert photos[0].date_modified.isoformat() == "2019-12-01T11:43:45.714123-04:00"
|
||||||
|
|||||||
@ -1,165 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
|
||||||
|
|
||||||
PHOTOS_DB = "./tests/Test-10.14.5.photoslibrary/database/photos.db"
|
|
||||||
KEYWORDS = [
|
|
||||||
"Kids",
|
|
||||||
"wedding",
|
|
||||||
"flowers",
|
|
||||||
"England",
|
|
||||||
"London",
|
|
||||||
"London 2018",
|
|
||||||
"St. James's Park",
|
|
||||||
"UK",
|
|
||||||
"United Kingdom",
|
|
||||||
]
|
|
||||||
PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON]
|
|
||||||
ALBUMS = ["Pumpkin Farm"]
|
|
||||||
KEYWORDS_DICT = {
|
|
||||||
"Kids": 4,
|
|
||||||
"wedding": 2,
|
|
||||||
"flowers": 1,
|
|
||||||
"England": 1,
|
|
||||||
"London": 1,
|
|
||||||
"London 2018": 1,
|
|
||||||
"St. James's Park": 1,
|
|
||||||
"UK": 1,
|
|
||||||
"United Kingdom": 1,
|
|
||||||
}
|
|
||||||
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1, _UNKNOWN_PERSON: 1}
|
|
||||||
ALBUM_DICT = {"Pumpkin Farm": 3}
|
|
||||||
|
|
||||||
|
|
||||||
def test_init():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
|
||||||
|
|
||||||
|
|
||||||
def test_db_version():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
|
||||||
assert photosdb.db_version == "4016"
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "Katie" in photosdb.persons
|
|
||||||
assert collections.Counter(PERSONS) == collections.Counter(photosdb.persons)
|
|
||||||
|
|
||||||
|
|
||||||
def test_keywords():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "wedding" in photosdb.keywords
|
|
||||||
assert collections.Counter(KEYWORDS) == collections.Counter(photosdb.keywords)
|
|
||||||
|
|
||||||
|
|
||||||
def test_album_names():
|
|
||||||
import osxphotos
|
|
||||||
import collections
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
assert "Pumpkin Farm" in photosdb.albums
|
|
||||||
assert collections.Counter(ALBUMS) == collections.Counter(photosdb.albums)
|
|
||||||
|
|
||||||
|
|
||||||
def test_keywords_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
keywords = photosdb.keywords_as_dict
|
|
||||||
assert keywords["wedding"] == 2
|
|
||||||
assert keywords == KEYWORDS_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_persons_as_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
persons = photosdb.persons_as_dict
|
|
||||||
assert persons["Maria"] == 1
|
|
||||||
assert persons == PERSONS_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_albums_as_dict():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
albums = photosdb.albums_as_dict
|
|
||||||
assert albums["Pumpkin Farm"] == 3
|
|
||||||
assert albums == ALBUM_DICT
|
|
||||||
|
|
||||||
|
|
||||||
def test_attributes():
|
|
||||||
import datetime
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["15uNd7%8RguTEgNPKHfTWw"])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.keywords == ["Kids"]
|
|
||||||
assert p.original_filename == "Pumkins2.jpg"
|
|
||||||
assert p.filename == "Pumkins2.jpg"
|
|
||||||
assert p.date == datetime.datetime(
|
|
||||||
2018, 9, 28, 16, 7, 7, 0, datetime.timezone(datetime.timedelta(seconds=-14400))
|
|
||||||
)
|
|
||||||
assert p.description == "Girl holding pumpkin"
|
|
||||||
assert p.title == "I found one!"
|
|
||||||
assert p.albums == ["Pumpkin Farm"]
|
|
||||||
assert p.persons == ["Katie"]
|
|
||||||
assert p.path.endswith(
|
|
||||||
"/tests/Test-10.14.5.photoslibrary/Masters/2019/07/27/20190727-131650/Pumkins2.jpg"
|
|
||||||
)
|
|
||||||
assert p.ismissing == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_missing():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(uuid=["od0fmC7NQx+ayVr+%i06XA"])
|
|
||||||
assert len(photos) == 1
|
|
||||||
p = photos[0]
|
|
||||||
assert p.path == None
|
|
||||||
assert p.ismissing == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_count():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos()
|
|
||||||
assert len(photos) == 7
|
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_2():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
photos = photosdb.photos(keywords=["wedding"])
|
|
||||||
assert len(photos) == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_not_in_album():
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
|
||||||
|
|
||||||
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
|
|
||||||
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
|
|
||||||
photos2 = photosdb.photos(keywords=["Kids"])
|
|
||||||
photos3 = [p for p in photos2 if p not in photos1]
|
|
||||||
assert len(photos3) == 1
|
|
||||||
assert photos3[0].uuid == "od0fmC7NQx+ayVr+%i06XA"
|
|
||||||
@ -356,7 +356,7 @@ def photosdb_cloud():
|
|||||||
|
|
||||||
|
|
||||||
def test_lookup(photosdb_places):
|
def test_lookup(photosdb_places):
|
||||||
""" Test that a lookup is returned for every possible value """
|
"""Test that a lookup is returned for every possible value"""
|
||||||
import re
|
import re
|
||||||
from osxphotos.phototemplate import TEMPLATE_SUBSTITUTIONS, PhotoTemplate
|
from osxphotos.phototemplate import TEMPLATE_SUBSTITUTIONS, PhotoTemplate
|
||||||
|
|
||||||
@ -370,7 +370,7 @@ def test_lookup(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_lookup_multi(photosdb_places):
|
def test_lookup_multi(photosdb_places):
|
||||||
""" Test that a lookup is returned for every possible value """
|
"""Test that a lookup is returned for every possible value"""
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from osxphotos.phototemplate import (
|
from osxphotos.phototemplate import (
|
||||||
@ -390,7 +390,7 @@ def test_lookup_multi(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst(photosdb_places):
|
def test_subst(photosdb_places):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, "en_US")
|
locale.setlocale(locale.LC_ALL, "en_US")
|
||||||
@ -402,7 +402,7 @@ def test_subst(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_date_modified(photosdb_places):
|
def test_subst_date_modified(photosdb_places):
|
||||||
""" Test that substitutions are correct for date modified """
|
"""Test that substitutions are correct for date modified"""
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, "en_US")
|
locale.setlocale(locale.LC_ALL, "en_US")
|
||||||
@ -414,7 +414,7 @@ def test_subst_date_modified(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_date_not_modified(photosdb_places):
|
def test_subst_date_not_modified(photosdb_places):
|
||||||
""" Test that substitutions are correct for date modified when photo isn't modified """
|
"""Test that substitutions are correct for date modified when photo isn't modified"""
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, "en_US")
|
locale.setlocale(locale.LC_ALL, "en_US")
|
||||||
@ -426,7 +426,7 @@ def test_subst_date_not_modified(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_locale_1(photosdb_places):
|
def test_subst_locale_1(photosdb_places):
|
||||||
""" Test that substitutions are correct in user locale"""
|
"""Test that substitutions are correct in user locale"""
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
# osxphotos.template sets local on load so set the environment first
|
# osxphotos.template sets local on load so set the environment first
|
||||||
@ -441,7 +441,7 @@ def test_subst_locale_1(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_locale_2(photosdb_places):
|
def test_subst_locale_2(photosdb_places):
|
||||||
""" Test that substitutions are correct in user locale"""
|
"""Test that substitutions are correct in user locale"""
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -462,7 +462,7 @@ def test_subst_locale_2(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_default_val(photosdb_places):
|
def test_subst_default_val(photosdb_places):
|
||||||
""" Test substitution with default value specified """
|
"""Test substitution with default value specified"""
|
||||||
import locale
|
import locale
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
@ -475,7 +475,7 @@ def test_subst_default_val(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_default_val_2(photosdb_places):
|
def test_subst_default_val_2(photosdb_places):
|
||||||
""" Test substitution with ',' but no default value """
|
"""Test substitution with ',' but no default value"""
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, "en_US")
|
locale.setlocale(locale.LC_ALL, "en_US")
|
||||||
@ -487,7 +487,7 @@ def test_subst_default_val_2(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_unknown_val(photosdb_places):
|
def test_subst_unknown_val(photosdb_places):
|
||||||
""" Test substitution with unknown value specified """
|
"""Test substitution with unknown value specified"""
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, "en_US")
|
locale.setlocale(locale.LC_ALL, "en_US")
|
||||||
@ -511,7 +511,7 @@ def test_subst_unknown_val(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_unknown_val_with_default(photosdb_places):
|
def test_subst_unknown_val_with_default(photosdb_places):
|
||||||
""" Test substitution with unknown value specified """
|
"""Test substitution with unknown value specified"""
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, "en_US")
|
locale.setlocale(locale.LC_ALL, "en_US")
|
||||||
@ -524,7 +524,7 @@ def test_subst_unknown_val_with_default(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_1_1_2(photosdb):
|
def test_subst_multi_1_1_2(photosdb):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
@ -537,20 +537,24 @@ def test_subst_multi_1_1_2(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_2_1_1(photosdb):
|
def test_subst_multi_2_1_1(photosdb):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
# 2 albums, 1 keyword, 1 person
|
# 2 albums, 1 keyword, 1 person
|
||||||
|
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["2_1_1"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["2_1_1"]])[0]
|
||||||
|
|
||||||
template = "{created.year}/{album}/{keyword}/{person}"
|
template = "{created.year}/{album}/{keyword}/{person}"
|
||||||
expected = ["2018/Pumpkin Farm/Kids/Katie", "2018/Test Album/Kids/Katie"]
|
expected = [
|
||||||
|
"2018/Pumpkin Farm/Kids/Katie",
|
||||||
|
"2018/Test Album/Kids/Katie",
|
||||||
|
"2018/Multi Keyword/Kids/Katie",
|
||||||
|
]
|
||||||
rendered, _ = photo.render_template(template)
|
rendered, _ = photo.render_template(template)
|
||||||
assert sorted(rendered) == sorted(expected)
|
assert sorted(rendered) == sorted(expected)
|
||||||
|
|
||||||
|
|
||||||
def test_subst_multi_2_1_1_single(photosdb):
|
def test_subst_multi_2_1_1_single(photosdb):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
# 2 albums, 1 keyword, 1 person but only do keywords
|
# 2 albums, 1 keyword, 1 person but only do keywords
|
||||||
|
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
@ -563,7 +567,7 @@ def test_subst_multi_2_1_1_single(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_0_2_0(photosdb):
|
def test_subst_multi_0_2_0(photosdb):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
# 0 albums, 2 keywords, 0 persons
|
# 0 albums, 2 keywords, 0 persons
|
||||||
|
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
@ -576,7 +580,7 @@ def test_subst_multi_0_2_0(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_0_2_0_single(photosdb):
|
def test_subst_multi_0_2_0_single(photosdb):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
# 0 albums, 2 keywords, 0 persons, but only do albums
|
# 0 albums, 2 keywords, 0 persons, but only do albums
|
||||||
|
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
@ -589,7 +593,7 @@ def test_subst_multi_0_2_0_single(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_0_2_0_default_val(photosdb):
|
def test_subst_multi_0_2_0_default_val(photosdb):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
# 0 albums, 2 keywords, 0 persons, default vals provided
|
# 0 albums, 2 keywords, 0 persons, default vals provided
|
||||||
|
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
@ -602,7 +606,7 @@ def test_subst_multi_0_2_0_default_val(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_0_2_0_default_val_unknown_val(photosdb):
|
def test_subst_multi_0_2_0_default_val_unknown_val(photosdb):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
# 0 albums, 2 keywords, 0 persons, default vals provided, unknown val in template
|
# 0 albums, 2 keywords, 0 persons, default vals provided, unknown val in template
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
@ -620,7 +624,7 @@ def test_subst_multi_0_2_0_default_val_unknown_val(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_0_2_0_default_val_unknown_val_2(photosdb):
|
def test_subst_multi_0_2_0_default_val_unknown_val_2(photosdb):
|
||||||
""" Test that substitutions are correct """
|
"""Test that substitutions are correct"""
|
||||||
# 0 albums, 2 keywords, 0 persons, default vals provided, unknown val in template
|
# 0 albums, 2 keywords, 0 persons, default vals provided, unknown val in template
|
||||||
|
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
@ -637,7 +641,7 @@ def test_subst_multi_0_2_0_default_val_unknown_val_2(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_folder_albums_1(photosdb):
|
def test_subst_multi_folder_albums_1(photosdb):
|
||||||
""" Test substitutions for folder_album are correct """
|
"""Test substitutions for folder_album are correct"""
|
||||||
|
|
||||||
# photo in an album in a folder
|
# photo in an album in a folder
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_1"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_1"]])[0]
|
||||||
@ -653,7 +657,7 @@ def test_subst_multi_folder_albums_1(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_folder_albums_1_path_sep(photosdb):
|
def test_subst_multi_folder_albums_1_path_sep(photosdb):
|
||||||
""" Test substitutions for folder_album are correct with custom PATH_SEP """
|
"""Test substitutions for folder_album are correct with custom PATH_SEP"""
|
||||||
|
|
||||||
# photo in an album in a folder
|
# photo in an album in a folder
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_1"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_1"]])[0]
|
||||||
@ -669,7 +673,7 @@ def test_subst_multi_folder_albums_1_path_sep(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_folder_albums_1_path_sep_lower(photosdb):
|
def test_subst_multi_folder_albums_1_path_sep_lower(photosdb):
|
||||||
""" Test substitutions for folder_album are correct with custom PATH_SEP """
|
"""Test substitutions for folder_album are correct with custom PATH_SEP"""
|
||||||
|
|
||||||
# photo in an album in a folder
|
# photo in an album in a folder
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_1"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_1"]])[0]
|
||||||
@ -685,31 +689,31 @@ def test_subst_multi_folder_albums_1_path_sep_lower(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_folder_albums_2(photosdb):
|
def test_subst_multi_folder_albums_2(photosdb):
|
||||||
""" Test substitutions for folder_album are correct """
|
"""Test substitutions for folder_album are correct"""
|
||||||
|
|
||||||
# photo in an album in a folder
|
# photo in an album in a folder
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_no_folder"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_no_folder"]])[0]
|
||||||
template = "{folder_album}"
|
template = "{folder_album}"
|
||||||
expected = ["Pumpkin Farm", "Test Album"]
|
expected = ["Multi Keyword", "Pumpkin Farm", "Test Album"]
|
||||||
rendered, unknown = photo.render_template(template)
|
rendered, unknown = photo.render_template(template)
|
||||||
assert sorted(rendered) == sorted(expected)
|
assert sorted(rendered) == sorted(expected)
|
||||||
assert unknown == []
|
assert unknown == []
|
||||||
|
|
||||||
|
|
||||||
def test_subst_multi_folder_albums_2_path_sep(photosdb):
|
def test_subst_multi_folder_albums_2_path_sep(photosdb):
|
||||||
""" Test substitutions for folder_album are correct with custom PATH_SEP """
|
"""Test substitutions for folder_album are correct with custom PATH_SEP"""
|
||||||
|
|
||||||
# photo in an album in a folder
|
# photo in an album in a folder
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_no_folder"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_no_folder"]])[0]
|
||||||
template = "{folder_album(:)}"
|
template = "{folder_album(:)}"
|
||||||
expected = ["Pumpkin Farm", "Test Album"]
|
expected = ["Multi Keyword", "Pumpkin Farm", "Test Album"]
|
||||||
rendered, unknown = photo.render_template(template)
|
rendered, unknown = photo.render_template(template)
|
||||||
assert sorted(rendered) == sorted(expected)
|
assert sorted(rendered) == sorted(expected)
|
||||||
assert unknown == []
|
assert unknown == []
|
||||||
|
|
||||||
|
|
||||||
def test_subst_multi_folder_albums_3(photosdb_14_6):
|
def test_subst_multi_folder_albums_3(photosdb_14_6):
|
||||||
""" Test substitutions for folder_album on < Photos 5 """
|
"""Test substitutions for folder_album on < Photos 5"""
|
||||||
|
|
||||||
# photo in an album in a folder
|
# photo in an album in a folder
|
||||||
photo = photosdb_14_6.photos(uuid=[UUID_DICT["mojave_album_1"]])[0]
|
photo = photosdb_14_6.photos(uuid=[UUID_DICT["mojave_album_1"]])[0]
|
||||||
@ -721,7 +725,7 @@ def test_subst_multi_folder_albums_3(photosdb_14_6):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_folder_albums_3_path_sep(photosdb_14_6):
|
def test_subst_multi_folder_albums_3_path_sep(photosdb_14_6):
|
||||||
""" Test substitutions for folder_album on < Photos 5 with custom PATH_SEP """
|
"""Test substitutions for folder_album on < Photos 5 with custom PATH_SEP"""
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
# photo in an album in a folder
|
# photo in an album in a folder
|
||||||
@ -734,7 +738,7 @@ def test_subst_multi_folder_albums_3_path_sep(photosdb_14_6):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_multi_folder_albums_4_path_sep_lower(photosdb_14_6):
|
def test_subst_multi_folder_albums_4_path_sep_lower(photosdb_14_6):
|
||||||
""" Test substitutions for folder_album on < Photos 5 with custom PATH_SEP """
|
"""Test substitutions for folder_album on < Photos 5 with custom PATH_SEP"""
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
# photo in an album in a folder
|
# photo in an album in a folder
|
||||||
@ -747,7 +751,7 @@ def test_subst_multi_folder_albums_4_path_sep_lower(photosdb_14_6):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_strftime(photosdb_places):
|
def test_subst_strftime(photosdb_places):
|
||||||
""" Test that strftime substitutions are correct """
|
"""Test that strftime substitutions are correct"""
|
||||||
import locale
|
import locale
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
@ -762,7 +766,7 @@ def test_subst_strftime(photosdb_places):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_expand_inplace_1(photosdb):
|
def test_subst_expand_inplace_1(photosdb):
|
||||||
""" Test that substitutions are correct when expand_inplace=True """
|
"""Test that substitutions are correct when expand_inplace=True"""
|
||||||
|
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
||||||
@ -774,7 +778,7 @@ def test_subst_expand_inplace_1(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_expand_inplace_2(photosdb):
|
def test_subst_expand_inplace_2(photosdb):
|
||||||
""" Test that substitutions are correct when expand_inplace=True """
|
"""Test that substitutions are correct when expand_inplace=True"""
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
||||||
|
|
||||||
@ -785,7 +789,7 @@ def test_subst_expand_inplace_2(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_subst_expand_inplace_3(photosdb):
|
def test_subst_expand_inplace_3(photosdb):
|
||||||
""" Test that substitutions are correct when expand_inplace=True and inplace_sep specified"""
|
"""Test that substitutions are correct when expand_inplace=True and inplace_sep specified"""
|
||||||
# one album, one keyword, two persons
|
# one album, one keyword, two persons
|
||||||
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
||||||
|
|
||||||
@ -807,7 +811,7 @@ def test_comment(photosdb_comments):
|
|||||||
|
|
||||||
|
|
||||||
def test_media_type(photosdb_cloud):
|
def test_media_type(photosdb_cloud):
|
||||||
""" test {media_type} template """
|
"""test {media_type} template"""
|
||||||
|
|
||||||
for field, uuid in UUID_MEDIA_TYPE.items():
|
for field, uuid in UUID_MEDIA_TYPE.items():
|
||||||
if uuid is not None:
|
if uuid is not None:
|
||||||
@ -817,7 +821,7 @@ def test_media_type(photosdb_cloud):
|
|||||||
|
|
||||||
|
|
||||||
def test_media_type_default(photosdb_cloud):
|
def test_media_type_default(photosdb_cloud):
|
||||||
""" test {media_type,photo=foo} template style """
|
"""test {media_type,photo=foo} template style"""
|
||||||
|
|
||||||
for field, uuid in UUID_MEDIA_TYPE.items():
|
for field, uuid in UUID_MEDIA_TYPE.items():
|
||||||
if uuid is not None:
|
if uuid is not None:
|
||||||
@ -827,7 +831,7 @@ def test_media_type_default(photosdb_cloud):
|
|||||||
|
|
||||||
|
|
||||||
def test_bool_values(photosdb_cloud):
|
def test_bool_values(photosdb_cloud):
|
||||||
""" test {bool?TRUE,FALSE} template values """
|
"""test {bool?TRUE,FALSE} template values"""
|
||||||
|
|
||||||
for field, uuid in UUID_BOOL_VALUES.items():
|
for field, uuid in UUID_BOOL_VALUES.items():
|
||||||
if uuid is not None:
|
if uuid is not None:
|
||||||
@ -840,7 +844,7 @@ def test_bool_values(photosdb_cloud):
|
|||||||
|
|
||||||
|
|
||||||
def test_bool_values_not(photosdb_cloud):
|
def test_bool_values_not(photosdb_cloud):
|
||||||
""" test {bool?TRUE,FALSE} template values for FALSE values """
|
"""test {bool?TRUE,FALSE} template values for FALSE values"""
|
||||||
|
|
||||||
for field, uuid in UUID_BOOL_VALUES_NOT.items():
|
for field, uuid in UUID_BOOL_VALUES_NOT.items():
|
||||||
if uuid is not None:
|
if uuid is not None:
|
||||||
@ -850,7 +854,7 @@ def test_bool_values_not(photosdb_cloud):
|
|||||||
|
|
||||||
|
|
||||||
def test_partial_match(photosdb_cloud):
|
def test_partial_match(photosdb_cloud):
|
||||||
""" test that template successfully rejects a field that is superset of valid field """
|
"""test that template successfully rejects a field that is superset of valid field"""
|
||||||
|
|
||||||
for uuid in COMMENT_UUID_DICT:
|
for uuid in COMMENT_UUID_DICT:
|
||||||
photo = photosdb_cloud.get_photo(uuid)
|
photo = photosdb_cloud.get_photo(uuid)
|
||||||
@ -865,7 +869,7 @@ def test_partial_match(photosdb_cloud):
|
|||||||
|
|
||||||
|
|
||||||
def test_expand_in_place_with_delim(photosdb):
|
def test_expand_in_place_with_delim(photosdb):
|
||||||
""" Test that substitutions are correct when {DELIM+FIELD} format used """
|
"""Test that substitutions are correct when {DELIM+FIELD} format used"""
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||||
@ -876,7 +880,7 @@ def test_expand_in_place_with_delim(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_expand_in_place_with_delim_single_value(photosdb):
|
def test_expand_in_place_with_delim_single_value(photosdb):
|
||||||
""" Test that single-value substitutions are correct when {DELIM+FIELD} format used """
|
"""Test that single-value substitutions are correct when {DELIM+FIELD} format used"""
|
||||||
|
|
||||||
photo = photosdb.get_photo(UUID_TITLE)
|
photo = photosdb.get_photo(UUID_TITLE)
|
||||||
|
|
||||||
@ -895,7 +899,7 @@ def test_exiftool_template(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_hdr(photosdb):
|
def test_hdr(photosdb):
|
||||||
""" Test hdr """
|
"""Test hdr"""
|
||||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||||
photomock = PhotoInfoMock(photo, hdr="hdr")
|
photomock = PhotoInfoMock(photo, hdr="hdr")
|
||||||
rendered, _ = photomock.render_template("{hdr}")
|
rendered, _ = photomock.render_template("{hdr}")
|
||||||
@ -903,7 +907,7 @@ def test_hdr(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_edited(photosdb):
|
def test_edited(photosdb):
|
||||||
""" Test edited """
|
"""Test edited"""
|
||||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||||
photomock = PhotoInfoMock(photo, hasadjustments=True)
|
photomock = PhotoInfoMock(photo, hasadjustments=True)
|
||||||
rendered, _ = photomock.render_template("{edited}")
|
rendered, _ = photomock.render_template("{edited}")
|
||||||
@ -911,7 +915,7 @@ def test_edited(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_favorite(photosdb):
|
def test_favorite(photosdb):
|
||||||
""" Test favorite"""
|
"""Test favorite"""
|
||||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||||
photomock = PhotoInfoMock(photo, favorite=True)
|
photomock = PhotoInfoMock(photo, favorite=True)
|
||||||
rendered, _ = photomock.render_template("{favorite}")
|
rendered, _ = photomock.render_template("{favorite}")
|
||||||
@ -973,14 +977,14 @@ def test_conditional(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_function(photosdb):
|
def test_function(photosdb):
|
||||||
""" Test {function} """
|
"""Test {function}"""
|
||||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||||
rendered, _ = photo.render_template("{function:tests/template_function.py::foo}")
|
rendered, _ = photo.render_template("{function:tests/template_function.py::foo}")
|
||||||
assert rendered == [f"{photo.original_filename}-FOO"]
|
assert rendered == [f"{photo.original_filename}-FOO"]
|
||||||
|
|
||||||
|
|
||||||
def test_function_bad(photosdb):
|
def test_function_bad(photosdb):
|
||||||
""" Test invalid {function} """
|
"""Test invalid {function}"""
|
||||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
rendered, _ = photo.render_template(
|
rendered, _ = photo.render_template(
|
||||||
@ -989,7 +993,7 @@ def test_function_bad(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_function_filter(photosdb):
|
def test_function_filter(photosdb):
|
||||||
""" Test {field|function} filter"""
|
"""Test {field|function} filter"""
|
||||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||||
|
|
||||||
rendered, _ = photo.render_template(
|
rendered, _ = photo.render_template(
|
||||||
@ -1009,7 +1013,7 @@ def test_function_filter(photosdb):
|
|||||||
|
|
||||||
|
|
||||||
def test_function_filter_bad(photosdb):
|
def test_function_filter_bad(photosdb):
|
||||||
""" Test invalid {field|function} filter"""
|
"""Test invalid {field|function} filter"""
|
||||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
rendered, _ = photo.render_template(
|
rendered, _ = photo.render_template(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user