Cleaned up tests, fixed bug in PhotosDB.query

This commit is contained in:
Rhet Turnbull 2021-06-11 23:02:48 -07:00
parent 4b6c35b5f9
commit 0758f84dc4
48 changed files with 793 additions and 6017 deletions

View File

@ -1497,7 +1497,7 @@ Substitution Description
{lf} A line feed: '\n', alias for {newline}
{cr} A carriage return: '\r'
{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
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}|
|{cr}|A carriage return: '\r'|
|{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|
|{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|

View File

@ -80,7 +80,7 @@ class PhotoInfo:
@property
def filename(self):
""" filename of the picture """
"""filename of the picture"""
if (
self._db._db_version <= _PHOTOS_4_VERSION
and self.has_raw
@ -108,7 +108,7 @@ class PhotoInfo:
@property
def date(self):
""" image creation date as timezone aware datetime object """
"""image creation date as timezone aware datetime object"""
return self._info["imageDate"]
@property
@ -134,12 +134,12 @@ class PhotoInfo:
@property
def tzoffset(self):
""" timezone offset from UTC in seconds """
"""timezone offset from UTC in seconds"""
return self._info["imageTimeZoneOffsetSeconds"]
@property
def path(self):
""" absolute path on disk of the original picture """
"""absolute path on disk of the original picture"""
try:
return self._path
except AttributeError:
@ -211,7 +211,7 @@ class PhotoInfo:
@property
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 """
try:
@ -225,7 +225,7 @@ class PhotoInfo:
return self._path_edited
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:
# edited photos appear to always be converted to .jpeg and stored in
# library_name/resources/renders/X/UUID_1_201_a.jpeg
@ -283,7 +283,7 @@ class PhotoInfo:
return photopath
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:
raise RuntimeError("Wrong database format!")
@ -343,7 +343,7 @@ class PhotoInfo:
@property
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
# Unless "Copy Items to the Photos Library" is not checked
@ -413,17 +413,17 @@ class PhotoInfo:
@property
def description(self):
""" long / extended description of picture """
"""long / extended description of picture"""
return self._info["extendedDescription"]
@property
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"]]
@property
def person_info(self):
""" list of PersonInfo objects for person in picture """
"""list of PersonInfo objects for person in picture"""
try:
return self._personinfo
except AttributeError:
@ -434,7 +434,7 @@ class PhotoInfo:
@property
def face_info(self):
""" list of FaceInfo objects for faces in picture """
"""list of FaceInfo objects for faces in picture"""
try:
return self._faceinfo
except AttributeError:
@ -448,7 +448,7 @@ class PhotoInfo:
@property
def albums(self):
""" list of albums picture is contained in """
"""list of albums picture is contained in"""
try:
return self._albums
except AttributeError:
@ -460,7 +460,7 @@ class PhotoInfo:
@property
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:
return self._burst_albums
except AttributeError:
@ -473,7 +473,7 @@ class PhotoInfo:
@property
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:
return self._album_info
except AttributeError:
@ -485,7 +485,7 @@ class PhotoInfo:
@property
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:
return self._burst_album_info
except AttributeError:
@ -498,7 +498,7 @@ class PhotoInfo:
@property
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:
return self._import_info
except AttributeError:
@ -511,17 +511,17 @@ class PhotoInfo:
@property
def keywords(self):
""" list of keywords for picture """
"""list of keywords for picture"""
return self._info["keywords"]
@property
def title(self):
""" name / title of picture """
"""name / title of picture"""
return self._info["name"]
@property
def uuid(self):
""" UUID of picture """
"""UUID of picture"""
return self._uuid
@property
@ -539,12 +539,12 @@ class PhotoInfo:
@property
def hasadjustments(self):
""" True if picture has adjustments / edits """
"""True if picture has adjustments / edits"""
return self._info["hasAdjustments"] == 1
@property
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:
return None
@ -568,32 +568,32 @@ class PhotoInfo:
@property
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"
@property
def favorite(self):
""" True if picture is marked as favorite """
"""True if picture is marked as favorite"""
return self._info["favorite"] == 1
@property
def hidden(self):
""" True if picture is hidden """
"""True if picture is hidden"""
return self._info["hidden"] == 1
@property
def visible(self):
""" True if picture is visble """
"""True if picture is visble"""
return self._info["visible"]
@property
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"]
@property
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
# also update date_modified
trasheddate = self._info["trasheddate"]
@ -607,7 +607,7 @@ class PhotoInfo:
@property
def date_added(self):
""" Date photo was added to the database """
"""Date photo was added to the database"""
try:
return self._date_added
except AttributeError:
@ -624,7 +624,7 @@ class PhotoInfo:
@property
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)
@property
@ -720,27 +720,27 @@ class PhotoInfo:
@property
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"]
@property
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"]
@property
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)
@property
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)
@property
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)
@property
@ -760,7 +760,7 @@ class PhotoInfo:
@property
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"]
@property
@ -821,7 +821,7 @@ class PhotoInfo:
@property
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:
return self._path_derivatives_4()
@ -838,7 +838,7 @@ class PhotoInfo:
return [str(filename) for filename in files if filename.suffix != ".THM"]
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"]
if modelid is None:
return []
@ -875,42 +875,42 @@ class PhotoInfo:
@property
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"]
@property
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"]
@property
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"]
@property
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"]
@property
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"]
@property
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"]
@property
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"]
@property
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
# then memoizes the object in self._place to avoid recreating the object
@ -938,12 +938,12 @@ class PhotoInfo:
@property
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"]
@property
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
@property
@ -955,17 +955,17 @@ class PhotoInfo:
@property
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"]
@property
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"]
@property
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:
return self._info["orientation"]
@ -981,27 +981,27 @@ class PhotoInfo:
@property
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"]
@property
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"]
@property
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"]
@property
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"]
@property
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)
duplicates = []
try:
@ -1060,12 +1060,12 @@ class PhotoInfo:
@property
def _longitude(self):
""" Returns longitude, in degrees """
"""Returns longitude, in degrees"""
return self._info["longitude"]
@property
def _latitude(self):
""" Returns latitude, in degrees """
"""Returns latitude, in degrees"""
return self._info["latitude"]
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})"
def __str__(self):
""" string representation of PhotoInfo object """
"""string representation of PhotoInfo object"""
date_iso = self.date.isoformat()
date_modified_iso = (
@ -1166,7 +1166,7 @@ class PhotoInfo:
return yaml.dump(info, sort_keys=False)
def asdict(self):
""" return dict representation """
"""return dict representation"""
folders = {album.title: album.folder_names for album in self.album_info}
exif = dataclasses.asdict(self.exif_info) if self.exif_info else {}
@ -1242,7 +1242,7 @@ class PhotoInfo:
}
def json(self):
""" Return JSON representation """
"""Return JSON representation"""
def default(o):
if isinstance(o, (datetime.date, datetime.datetime)):
@ -1251,7 +1251,7 @@ class PhotoInfo:
return json.dumps(self.asdict(), sort_keys=True, default=default)
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)
# memoize their value once called in an instance variable (e.g. self._albums)
if isinstance(other, self.__class__):
@ -1263,5 +1263,9 @@ class PhotoInfo:
return False
def __ne__(self, other):
""" Compare two PhotoInfo objects for inequality """
"""Compare two PhotoInfo objects for inequality"""
return not self.__eq__(other)
def __hash__(self):
"""Make PhotoInfo hashable"""
return hash(self.uuid)

View File

@ -62,7 +62,7 @@ from .photosdb_utils import get_db_model_version, get_db_version
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
from ._photosdb_process_exif import _process_exifinfo
@ -78,7 +78,7 @@ class PhotosDB:
from ._photosdb_process_comments import _process_comments
def __init__(self, dbfile=None, verbose=None, exiftool=None):
""" Create a new PhotosDB object.
"""Create a new PhotosDB object.
Args:
dbfile: specify full path to photos library or photos.db; if None, will attempt to locate last library opened by Photos.
@ -326,7 +326,7 @@ class PhotosDB:
@property
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 = {
k: len(self._dbkeywords_keyword[k]) for k in self._dbkeywords_keyword.keys()
}
@ -336,7 +336,7 @@ class PhotosDB:
@property
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 = {}
for pk in self._dbfaces_pk:
fullname = self._dbpersons_pk[pk]["fullname"]
@ -349,7 +349,7 @@ class PhotosDB:
@property
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 = {}
album_keys = self._get_album_uuids(shared=False)
for album in album_keys:
@ -366,8 +366,8 @@ class PhotosDB:
@property
def albums_shared_as_dict(self):
""" 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 """
"""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"""
albums = {}
album_keys = self._get_album_uuids(shared=True)
@ -385,19 +385,19 @@ class PhotosDB:
@property
def keywords(self):
""" return list of keywords found in photos database """
"""return list of keywords found in photos database"""
keywords = self._dbkeywords_keyword.keys()
return list(keywords)
@property
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}
return list(persons)
@property
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:
return self._person_info
except AttributeError:
@ -408,7 +408,7 @@ class PhotosDB:
@property
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:
folders = [
FolderInfo(db=self, uuid=folder)
@ -429,7 +429,7 @@ class PhotosDB:
@property
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:
folder_names = [
folder["name"]
@ -450,7 +450,7 @@ class PhotosDB:
@property
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:
return self._album_info
except AttributeError:
@ -462,8 +462,8 @@ class PhotosDB:
@property
def album_info_shared(self):
""" 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 """
"""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"""
# if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
try:
return self._album_info_shared
@ -476,7 +476,7 @@ class PhotosDB:
@property
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
# Right now, they are treated as same album and photos are combined from albums with same name
@ -489,8 +489,8 @@ class PhotosDB:
@property
def albums_shared(self):
""" return list of shared albums found in photos database
only valid for Photos 5; on Photos <= 4, prints warning and returns empty list """
"""return list of shared albums found in photos database
only valid for Photos 5; on Photos <= 4, prints warning and returns empty list"""
# 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
@ -505,7 +505,7 @@ class PhotosDB:
@property
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:
return self._import_info
except AttributeError:
@ -517,21 +517,21 @@ class PhotosDB:
@property
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
@property
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)
@property
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
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:
tuple of (connection, cursor) to sqlite3 database
@ -539,7 +539,7 @@ class PhotosDB:
return _open_sql_file(self._tmp_db)
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 """
""" 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
@ -591,8 +591,8 @@ class PhotosDB:
# return dest_path
def _process_database4(self):
""" process the Photos database to extract info
works on Photos version <= 4.0 """
"""process the Photos database to extract info
works on Photos version <= 4.0"""
verbose = self._verbose
verbose("Processing database.")
@ -1543,15 +1543,15 @@ class PhotosDB:
logging.debug(pformat(self._dbphotos_burst))
def _build_album_folder_hierarchy_4(self, uuid, folders=None):
""" recursively build folder/album hierarchy
uuid: parent uuid of the album being processed
(parent uuid is a folder in RKFolders)
folders: dict holding the folder hierarchy
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
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
with kind = _PHOTOS_5_FOLDER_KIND """
"""recursively build folder/album hierarchy
uuid: parent uuid of the album being processed
(parent uuid is a folder in RKFolders)
folders: dict holding the folder hierarchy
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
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
with kind = _PHOTOS_5_FOLDER_KIND"""
parent_uuid = self._dbfolder_details[uuid]["parentFolderUuid"]
@ -1574,11 +1574,11 @@ class PhotosDB:
return folders
def _process_database5(self):
""" process the Photos database to extract info
works on Photos version 5 and version 6
"""process the Photos database to extract info
works on Photos version 5 and version 6
This is a big hairy 700 line function that should probably be refactored
but it works so don't touch it.
This is a big hairy 700 line function that should probably be refactored
but it works so don't touch it.
"""
if _debug():
@ -2448,9 +2448,9 @@ class PhotosDB:
logging.debug(pformat(self._dbphotos_burst))
def _build_album_folder_hierarchy_5(self, uuid, folders=None):
""" recursively build folder/album hierarchy
uuid: uuid of the album/folder being processed
folders: dict holding the folder hierarchy """
"""recursively build folder/album hierarchy
uuid: uuid of the album/folder being processed
folders: dict holding the folder hierarchy"""
# get parent uuid
parent = self._dbalbum_details[uuid]["parentfolder"]
@ -2471,17 +2471,17 @@ class PhotosDB:
return folders
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:
return self._album_folder_hierarchy_list_4(album_uuid)
else:
return self._album_folder_hierarchy_list_5(album_uuid)
def _album_folder_hierarchy_list_4(self, album_uuid):
""" return hierarchical list of folder names album_uuid is contained in
the folder list is in form:
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders """
"""return hierarchical list of folder names album_uuid is contained in
the folder list is in form:
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders"""
try:
folders = self._dbalbum_folders[album_uuid]
except KeyError:
@ -2489,7 +2489,7 @@ class PhotosDB:
return []
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:
# empty folder dict (album has no folder hierarchy)
return []
@ -2515,10 +2515,10 @@ class PhotosDB:
return hierarchy
def _album_folder_hierarchy_list_5(self, album_uuid):
""" return hierarchical list of folder names album_uuid is contained in
the folder list is in form:
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders """
"""return hierarchical list of folder names album_uuid is contained in
the folder list is in form:
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders"""
try:
folders = self._dbalbum_folders[album_uuid]
except KeyError:
@ -2526,7 +2526,7 @@ class PhotosDB:
return []
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:
# empty folder dict (album has no folder hierarchy)
@ -2558,15 +2558,15 @@ class PhotosDB:
return self._album_folder_hierarchy_folderinfo_5(album_uuid)
def _album_folder_hierarchy_folderinfo_4(self, album_uuid):
""" return hierarchical list of FolderInfo objects album_uuid is contained in
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders """
"""return hierarchical list of FolderInfo objects album_uuid is contained in
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders"""
# title = photosdb._dbalbum_details[album_uuid]["title"]
folders = self._dbalbum_folders[album_uuid]
# logging.warning(f"uuid = {album_uuid}, folder = {folders}")
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}")
if not folders:
# empty folder dict (album has no folder hierarchy)
@ -2592,14 +2592,14 @@ class PhotosDB:
return hierarchy
def _album_folder_hierarchy_folderinfo_5(self, album_uuid):
""" return hierarchical list of FolderInfo objects album_uuid is contained in
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders """
"""return hierarchical list of FolderInfo objects album_uuid is contained in
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders"""
# title = photosdb._dbalbum_details[album_uuid]["title"]
folders = self._dbalbum_folders[album_uuid]
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:
# empty folder dict (album has no folder hierarchy)
@ -2624,7 +2624,7 @@ class PhotosDB:
return hierarchy
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
@ -2688,7 +2688,7 @@ class PhotosDB:
return album_list
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.
Filters out albums in the trash and any special album types
@ -2714,7 +2714,7 @@ class PhotosDB:
to_date=None,
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 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)
@ -2839,7 +2839,7 @@ class PhotosDB:
return photoinfo
def get_photo(self, uuid):
""" Returns a single photo matching uuid
"""Returns a single photo matching uuid
Arguments:
uuid: the UUID of photo to get
@ -2854,7 +2854,7 @@ class PhotosDB:
# TODO: add to docs and test
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.
This is faster than using PhotosDB.photos if you have list of UUIDs.
Returns photos regardless of intrash state.
@ -3228,7 +3228,7 @@ class PhotosDB:
return photos
def _duplicate_signature(self, uuid):
""" Compute a signature for finding possible duplicates """
"""Compute a signature for finding possible duplicates"""
return (
self._dbphotos[uuid]["original_filesize"],
self._dbphotos[uuid]["imageDate"],
@ -3249,8 +3249,8 @@ class PhotosDB:
return False
def __len__(self):
""" Returns number of photos in the database
Includes recently deleted photos and non-selected burst images
"""Returns number of photos in the database
Includes recently deleted photos and non-selected burst images
"""
return len(self._dbphotos)
@ -3280,4 +3280,4 @@ def _get_photos_by_attribute(photos, attribute, values, ignore_case):
else:
for x in values:
photos_search.extend(p for p in photos if x in getattr(p, attribute))
return photos_search
return list(set(photos_search))

View File

@ -5,9 +5,9 @@
<key>hostname</key>
<string>Rhets-MacBook-Pro.local</string>
<key>hostuuid</key>
<string>9575E48B-8D5F-5654-ABAC-4431B1167324</string>
<string>585B80BF-8D1F-55EF-A9E8-6CF4E5523959</string>
<key>pid</key>
<integer>86501</integer>
<integer>570</integer>
<key>processname</key>
<string>photolibraryd</string>
<key>uid</key>

View File

@ -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"}]

View File

@ -18,6 +18,7 @@
<dc:subject>
<rdf:Bag>
<rdf:li>Kids</rdf:li>
<rdf:li>Multi Keyword</rdf:li>
<rdf:li>Pumpkin Farm</rdf:li>
<rdf:li>Test Album</rdf:li>
</rdf:Bag>
@ -37,6 +38,7 @@
<digiKam:TagsList>
<rdf:Seq>
<rdf:li>Kids</rdf:li>
<rdf:li>Multi Keyword</rdf:li>
<rdf:li>Pumpkin Farm</rdf:li>
<rdf:li>Test Album</rdf:li>
</rdf:Seq>

View File

@ -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"}]

View File

@ -19,6 +19,7 @@
<rdf:Bag>
<rdf:li>2018</rdf:li>
<rdf:li>Kids</rdf:li>
<rdf:li>Multi Keyword</rdf:li>
<rdf:li>Pumpkin Farm</rdf:li>
<rdf:li>Test Album</rdf:li>
</rdf:Bag>
@ -39,6 +40,7 @@
<rdf:Seq>
<rdf:li>2018</rdf:li>
<rdf:li>Kids</rdf:li>
<rdf:li>Multi Keyword</rdf:li>
<rdf:li>Pumpkin Farm</rdf:li>
<rdf:li>Test Album</rdf:li>
</rdf:Seq>

View File

@ -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"}]

View File

@ -20,6 +20,7 @@
<rdf:li>AlbumInFolder</rdf:li>
<rdf:li>I have a deleted twin</rdf:li>
<rdf:li>Maria</rdf:li>
<rdf:li>Multi Keyword</rdf:li>
<rdf:li>wedding</rdf:li>
</rdf:Bag>
</dc:subject>
@ -40,6 +41,7 @@
<rdf:li>AlbumInFolder</rdf:li>
<rdf:li>I have a deleted twin</rdf:li>
<rdf:li>Maria</rdf:li>
<rdf:li>Multi Keyword</rdf:li>
<rdf:li>wedding</rdf:li>
</rdf:Seq>
</digiKam:TagsList>

View File

@ -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"}]

View File

@ -21,6 +21,7 @@
<rdf:li>Folder1/SubFolder2/AlbumInFolder</rdf:li>
<rdf:li>I have a deleted twin</rdf:li>
<rdf:li>Maria</rdf:li>
<rdf:li>Multi Keyword</rdf:li>
<rdf:li>wedding</rdf:li>
</rdf:Bag>
</dc:subject>
@ -42,6 +43,7 @@
<rdf:li>Folder1/SubFolder2/AlbumInFolder</rdf:li>
<rdf:li>I have a deleted twin</rdf:li>
<rdf:li>Maria</rdf:li>
<rdf:li>Multi Keyword</rdf:li>
<rdf:li>wedding</rdf:li>
</rdf:Seq>
</digiKam:TagsList>

View File

@ -1,5 +1,7 @@
import pytest
import osxphotos
from osxphotos._constants import _UNKNOWN_PERSON
PHOTOS_DB = "./tests/Test-10.15.4.photoslibrary/database/photos.db"
@ -61,11 +63,12 @@ UUID_DICT = {
}
def test_folders_1():
import osxphotos
@pytest.fixture(scope="module")
def photosdb():
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_1(photosdb):
# top level folders
folders = photosdb.folder_info
assert len(folders) == len(TOP_LEVEL_FOLDERS)
@ -75,31 +78,19 @@ def test_folders_1():
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folder_names():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folder_names(photosdb):
# check folder names
folder_names = photosdb.folders
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folders_len():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_len(photosdb):
# top level folders
folders = photosdb.folder_info
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
def test_folders_children():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_children(photosdb):
# top level folders
folders = photosdb.folder_info
@ -118,11 +109,7 @@ def test_folders_children():
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folders_parent():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_parent(photosdb):
# top level folders
folders = photosdb.folder_info
@ -135,11 +122,7 @@ def test_folders_parent():
assert child.parent.uuid == folder.uuid
def test_folders_albums():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_albums(photosdb):
# top level folders
folders = photosdb.folder_info
@ -156,11 +139,7 @@ def test_folders_albums():
########## Test AlbumInfo ##########
def test_albums_1():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_1(photosdb):
albums = photosdb.album_info
assert len(albums) == len(ALBUM_NAMES)
@ -169,11 +148,7 @@ def test_albums_1():
assert sorted(album_names) == sorted(ALBUM_NAMES)
def test_albums_parent():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_parent(photosdb):
albums = photosdb.album_info
for album in albums:
@ -181,11 +156,7 @@ def test_albums_parent():
assert parent == ALBUM_PARENT_DICT[album.title]
def test_albums_folder_names():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_folder_names(photosdb):
albums = photosdb.album_info
for album in albums:
@ -193,11 +164,7 @@ def test_albums_folder_names():
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
def test_albums_folders():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_folders(photosdb):
albums = photosdb.album_info
for album in albums:
folders = album.folder_list
@ -205,22 +172,14 @@ def test_albums_folders():
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
def test_albums_len():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_len(photosdb):
albums = photosdb.album_info
for album in albums:
assert len(album) == ALBUM_LEN_DICT[album.title]
def test_albums_photos():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_photos(photosdb):
albums = photosdb.album_info
for album in albums:
@ -231,12 +190,9 @@ def test_albums_photos():
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
def test_album_dates():
""" Test album date methods """
def test_album_dates(photosdb):
"""Test album date methods"""
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]
assert album.creation_date == datetime.datetime(
@ -271,34 +227,24 @@ def test_album_dates():
)
def test_photoinfo_albums():
""" Test PhotoInfo.albums """
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_photoinfo_albums(photosdb):
"""Test PhotoInfo.albums"""
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
albums = photos[0].albums
assert "Pumpkin Farm" in albums
def test_photoinfo_albums_2():
""" Test that PhotoInfo.albums returns only number albums expected """
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_photoinfo_albums_2(photosdb):
"""Test that PhotoInfo.albums returns only number albums expected"""
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
albums = photos[0].albums
assert len(albums) == 2
def test_photoinfo_album_info():
""" test PhotoInfo.album_info """
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_photoinfo_album_info(photosdb):
"""test PhotoInfo.album_info"""
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
album_info = photos[0].album_info

View File

@ -1,5 +1,7 @@
import pytest
import osxphotos
from osxphotos._constants import _UNKNOWN_PERSON
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 ##########
def test_folders_1():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_1(photosdb):
folders = photosdb.folders
# top level folders
@ -61,32 +64,20 @@ def test_folders_1():
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folder_names():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folder_names(photosdb):
# check folder names
folder_names = photosdb.folders
assert folder_names == TOP_LEVEL_FOLDERS
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folders_len():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_len(photosdb):
# top level folders
folders = photosdb.folder_info
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
def test_folders_children():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_children(photosdb):
# top level folders
folders = photosdb.folder_info
@ -105,11 +96,7 @@ def test_folders_children():
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folders_parent():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_parent(photosdb):
# top level folders
folders = photosdb.folder_info
@ -122,11 +109,7 @@ def test_folders_parent():
assert child.parent.uuid == folder.uuid
def test_folders_albums():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_albums(photosdb):
# top level folders
folders = photosdb.folder_info
@ -143,11 +126,7 @@ def test_folders_albums():
########## Test AlbumInfo ##########
def test_albums_1():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_1(photosdb):
albums = photosdb.album_info
assert len(albums) == len(ALBUM_NAMES)
@ -156,11 +135,7 @@ def test_albums_1():
assert sorted(album_names) == sorted(ALBUM_NAMES)
def test_albums_parent():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_parent(photosdb):
albums = photosdb.album_info
for album in albums:
@ -168,11 +143,7 @@ def test_albums_parent():
assert parent == ALBUM_PARENT_DICT[album.title]
def test_albums_folder_names():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_folder_names(photosdb):
albums = photosdb.album_info
for album in albums:
@ -180,11 +151,7 @@ def test_albums_folder_names():
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
def test_albums_folders():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_folders(photosdb):
albums = photosdb.album_info
for album in albums:
@ -193,22 +160,14 @@ def test_albums_folders():
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
def test_albums_len():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_len(photosdb):
albums = photosdb.album_info
for album in albums:
assert len(album) == ALBUM_LEN_DICT[album.title]
def test_albums_photos():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_photos(photosdb):
albums = photosdb.album_info
for album in albums:
@ -219,10 +178,7 @@ def test_albums_photos():
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
def test_photoinfo_albums():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_photoinfo_albums(photosdb):
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
albums = photos[0].albums

View File

@ -1,5 +1,7 @@
import pytest
import osxphotos
from osxphotos._constants import _UNKNOWN_PERSON
PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
@ -46,14 +48,16 @@ ALBUM_PHOTO_UUID_DICT = {
UUID_DICT = {"two_albums": "8SOE9s0XQVGsuq4ONohTng"}
@pytest.fixture(scope="module")
def photosdb():
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
######### Test FolderInfo ##########
def test_folders_1():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_1(photosdb):
folders = photosdb.folders
# top level folders
@ -65,32 +69,20 @@ def test_folders_1():
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folder_names():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folder_names(photosdb):
# check folder names
folder_names = photosdb.folders
assert folder_names == TOP_LEVEL_FOLDERS
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folders_len():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_len(photosdb):
# top level folders
folders = photosdb.folder_info
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
def test_folders_children():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_children(photosdb):
# top level folders
folders = photosdb.folder_info
@ -109,11 +101,7 @@ def test_folders_children():
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
def test_folders_parent():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_parent(photosdb):
# top level folders
folders = photosdb.folder_info
@ -126,11 +114,7 @@ def test_folders_parent():
assert child.parent.uuid == folder.uuid
def test_folders_albums():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_folders_albums(photosdb):
# top level folders
folders = photosdb.folder_info
@ -147,11 +131,7 @@ def test_folders_albums():
########## Test AlbumInfo ##########
def test_albums_1():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_1(photosdb):
albums = photosdb.album_info
assert len(albums) == 4
@ -160,11 +140,7 @@ def test_albums_1():
assert sorted(album_names) == sorted(ALBUM_NAMES)
def test_albums_parent():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_parent(photosdb):
albums = photosdb.album_info
for album in albums:
@ -172,11 +148,7 @@ def test_albums_parent():
assert parent == ALBUM_PARENT_DICT[album.title]
def test_albums_folder_names():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_folder_names(photosdb):
albums = photosdb.album_info
for album in albums:
@ -184,11 +156,7 @@ def test_albums_folder_names():
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
def test_albums_folders():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_folders(photosdb):
albums = photosdb.album_info
for album in albums:
@ -197,22 +165,14 @@ def test_albums_folders():
assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
def test_albums_len():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_len(photosdb):
albums = photosdb.album_info
for album in albums:
assert len(album) == ALBUM_LEN_DICT[album.title]
def test_albums_photos():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_albums_photos(photosdb):
albums = photosdb.album_info
for album in albums:
@ -223,20 +183,14 @@ def test_albums_photos():
assert photo.uuid in ALBUM_PHOTO_UUID_DICT[album.title]
def test_photoinfo_albums():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_photoinfo_albums(photosdb):
photos = photosdb.photos(uuid=ALBUM_PHOTO_UUID_DICT["Pumpkin Farm"])
albums = photos[0].albums
assert "Pumpkin Farm" in albums
def test_photoinfo_album_info():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_photoinfo_album_info(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
album_info = photos[0].album_info

File diff suppressed because it is too large Load Diff

View File

@ -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]

View File

@ -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

View File

@ -57,6 +57,7 @@ ALBUMS = [
"EmptyAlbum",
"2018-10 - Sponsion, Museum, Frühstück, Römermuseum",
"2019-10/11 Paris Clermont",
"Multi Keyword",
]
KEYWORDS_DICT = {
"Kids": 4,
@ -86,6 +87,7 @@ ALBUM_DICT = {
"EmptyAlbum": 0,
"2018-10 - Sponsion, Museum, Frühstück, Römermuseum": 1,
"2019-10/11 Paris Clermont": 1,
"Multi Keyword": 2,
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
UUID_DICT = {
@ -112,17 +114,19 @@ UUID_DICT = {
"movie": "D1359D09-1373-4F3B-B0E3-1A4DE573E4A3",
"description_newlines": "7F74DD34-5920-4DA3-B284-479887A34F66",
"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 = {
"not_visible": "4A836160-51B2-4E32-907D-ECDDB2CEC657", # IMG_9815.JPG
"burst": "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_selected": "75154738-83AA-4DCD-A913-632D5D1C0FEE", # IMG_9814.JPG
"burst_not_selected": "89E235DD-B9AC-4E8D-BDA2-986981CA7582", # IMG_9813.JPG
"burst_default": "F5E6BD24-B493-44E9-BDA2-7AD9D2CC8C9D", # IMG_9816.JPG
"burst_not_default": "75154738-83AA-4DCD-A913-632D5D1C0FEE", # IMG_9814.JPG
"not_visible": "4A836160-51B2-4E32-907D-ECDDB2CEC657", # IMG_9815.JPG
"burst": "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_selected": "75154738-83AA-4DCD-A913-632D5D1C0FEE", # IMG_9814.JPG
"burst_not_selected": "89E235DD-B9AC-4E8D-BDA2-986981CA7582", # IMG_9813.JPG
"burst_default": "F5E6BD24-B493-44E9-BDA2-7AD9D2CC8C9D", # IMG_9816.JPG
"burst_not_default": "75154738-83AA-4DCD-A913-632D5D1C0FEE", # IMG_9814.JPG
}
UUID_PUMPKIN_FARM = [
@ -374,7 +378,7 @@ def test_attributes(photosdb):
)
assert p.description == "Girl holding pumpkin"
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.path.endswith(
"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):
""" Test attributes including height, width, etc """
"""Test attributes including height, width, etc"""
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
assert len(photos) == 1
@ -403,7 +407,11 @@ def test_attributes_2(photosdb):
)
assert p.description == "Bride Wedding day"
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.path.endswith(
"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):
""" test visible """
"""test visible"""
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
assert len(photos) == 1
p = photos[0]
@ -469,7 +477,7 @@ def test_visible(photosdb):
def test_not_burst(photosdb):
""" test not burst """
"""test not burst"""
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
assert len(photos) == 1
p = photos[0]
@ -597,14 +605,14 @@ def test_count(photosdb):
def test_photos_intrash_1(photosdb):
""" test PhotosDB.photos(intrash=True) """
"""test PhotosDB.photos(intrash=True)"""
photos = photosdb.photos(intrash=True)
assert len(photos) == PHOTOS_IN_TRASH_LEN
def test_photos_intrash_2(photosdb):
""" test PhotosDB.photos(intrash=True) """
"""test PhotosDB.photos(intrash=True)"""
photos = photosdb.photos(intrash=True)
for p in photos:
@ -612,7 +620,7 @@ def test_photos_intrash_2(photosdb):
def test_photos_intrash_3(photosdb):
""" test PhotosDB.photos(intrash=False) """
"""test PhotosDB.photos(intrash=False)"""
photos = photosdb.photos(intrash=False)
for p in photos:
@ -620,7 +628,7 @@ def test_photos_intrash_3(photosdb):
def test_photoinfo_intrash_1(photosdb):
""" Test PhotoInfo.intrash """
"""Test PhotoInfo.intrash"""
p = photosdb.photos(uuid=[UUID_DICT["intrash"]], intrash=True)[0]
assert p.intrash
@ -628,14 +636,14 @@ def test_photoinfo_intrash_1(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"]])
assert not p
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]
assert p.intrash
@ -644,7 +652,7 @@ def test_photoinfo_intrash_3(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]
assert p.intrash
@ -653,7 +661,7 @@ def test_photoinfo_intrash_4(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]
assert p.intrash
@ -662,7 +670,7 @@ def test_photoinfo_intrash_5(photosdb):
def test_photoinfo_not_intrash(photosdb):
""" Test PhotoInfo.intrash """
"""Test PhotoInfo.intrash"""
p = photosdb.photos(uuid=[UUID_DICT["not_intrash"]])[0]
assert not p.intrash
@ -686,7 +694,7 @@ def test_keyword_not_in_album(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"])
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):
""" Test PhotosDB.get_db_connection """
"""Test PhotosDB.get_db_connection"""
conn, cursor = photosdb.get_db_connection()
@ -995,7 +1003,7 @@ def test_export_no_original_filename(photosdb):
def test_eq():
""" Test equality of two PhotoInfo objects """
"""Test equality of two PhotoInfo objects"""
photosdb1 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
@ -1005,7 +1013,7 @@ def test_eq():
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)
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
@ -1049,7 +1057,7 @@ def test_photosinfo_repr(photosdb):
def test_from_to_date(photosdb):
""" test from_date / to_date """
"""test from_date / to_date"""
os.environ["TZ"] = "US/Pacific"
time.tzset()
@ -1067,7 +1075,7 @@ def test_from_to_date(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"
time.tzset()
@ -1104,7 +1112,7 @@ def test_from_to_date_tz(photosdb):
def test_date_invalid():
""" Test date is invalid """
"""Test date is invalid"""
# doesn't run correctly with the module-level fixture
from datetime import datetime, timedelta, timezone
import osxphotos
@ -1119,7 +1127,7 @@ def test_date_invalid():
def test_date_modified_invalid(photosdb):
""" Test date modified is invalid """
"""Test date modified is invalid"""
photos = photosdb.photos(uuid=[UUID_DICT["date_invalid"]])
assert len(photos) == 1
@ -1128,14 +1136,14 @@ def test_date_modified_invalid(photosdb):
def test_import_session_count(photosdb):
""" Test PhotosDB.import_session """
"""Test PhotosDB.import_session"""
import_sessions = photosdb.import_info
assert len(import_sessions) == PHOTOS_DB_IMPORT_SESSIONS
def test_import_session_photo(photosdb):
""" Test photo.import_session """
"""Test photo.import_session"""
photo = photosdb.get_photo(UUID_DICT["import_session"])
import_session = photo.import_info
@ -1173,7 +1181,7 @@ def test_import_session_photo(photosdb):
def test_uti(photosdb):
""" test uti """
"""test uti"""
for uuid, uti in UTI_DICT.items():
photo = photosdb.get_photo(uuid)
@ -1182,7 +1190,7 @@ def test_uti(photosdb):
def test_raw(photosdb):
""" Test various raw properties """
"""Test various raw properties"""
for uuid, rawinfo in RAW_DICT.items():
photo = photosdb.get_photo(uuid)
@ -1195,7 +1203,7 @@ def test_raw(photosdb):
def test_verbose(capsys):
""" test verbose output in PhotosDB() """
"""test verbose output in PhotosDB()"""
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB, verbose=print)
captured = capsys.readouterr()
@ -1203,7 +1211,7 @@ def test_verbose(capsys):
def test_original_filename(photosdb):
""" test original filename """
"""test original filename"""
uuid = ORIGINAL_FILENAME_DICT["uuid"]
photo = photosdb.get_photo(uuid)
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
@pytest.mark.skipif(SKIP_TEST, reason="Skip if not running on author's local machine.")
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"])
assert not photo.visible
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.")
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"])
assert photo.visible
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.")
def test_burst_key(photosdb_local):
""" test burst_key """
"""test burst_key"""
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["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.")
def test_burst_selected(photosdb_local):
""" test burst_selected """
"""test burst_selected"""
photo = photosdb_local.get_photo(UUID_DICT_LOCAL["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.")
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"])
assert photo.burst_default_pick
@ -1266,7 +1274,7 @@ def test_burst_default_pic(photosdb_local):
def test_is_reference(photosdb):
""" test isreference """
"""test isreference"""
photo = photosdb.get_photo(UUID_IS_REFERENCE)
assert photo.isreference
@ -1275,7 +1283,7 @@ def test_is_reference(photosdb):
def test_adjustments(photosdb):
""" test adjustments/AdjustmentsInfo """
"""test adjustments/AdjustmentsInfo"""
from osxphotos.adjustmentsinfo import AdjustmentsInfo
photo = photosdb.get_photo(UUID_DICT["adjustments_info"])
@ -1337,14 +1345,14 @@ def test_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"])
assert photo.adjustments is None
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"])
exif = photo._exiftool_dict()
@ -1366,3 +1374,33 @@ def test_duplicates_2(photosdb):
photo = photosdb.get_photo(uuid=UUID_DICT["no_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

View File

@ -1,5 +1,7 @@
import pytest
import osxphotos
from osxphotos._constants import _UNKNOWN_PERSON
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}
def test_init():
import osxphotos
@pytest.fixture(scope="module")
def photosdb():
return osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_init(photosdb):
assert isinstance(photosdb, osxphotos.PhotosDB)
def test_db_version():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_db_version(photosdb):
assert photosdb.db_version == "3301"
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
def test_persons():
import osxphotos
def test_persons(photosdb):
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
def test_keywords(photosdb):
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
def test_album_names(photosdb):
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)
def test_keywords_dict(photosdb):
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)
def test_persons_as_dict(photosdb):
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)
def test_albums_as_dict(photosdb):
albums = photosdb.albums_as_dict
assert albums["Pumpkin Farm"] == 3
assert albums == ALBUM_DICT
def test_attributes():
def test_attributes(photosdb):
import datetime
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=["RWmFYiDjSyKjeK8Pfna0Eg"])
assert len(photos) == 1
p = photos[0]
@ -124,38 +108,25 @@ def test_attributes():
assert p.ismissing == False
def test_missing():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_missing(photosdb):
photos = photosdb.photos(uuid=["6iAZJP7ZQ5iXxapoJb3ytA"])
assert len(photos) == 1
p = photos[0]
assert p.path == None
assert p.ismissing == True
assert p.path is None
assert p.ismissing
def test_count():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_count(photosdb):
photos = photosdb.photos()
assert len(photos) == 7
def test_keyword_2():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_keyword_2(photosdb):
photos = photosdb.photos(keywords=["wedding"])
assert len(photos) == 2
def test_keyword_not_in_album():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_keyword_not_in_album(photosdb):
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
photos2 = photosdb.photos(keywords=["Kids"])

View File

@ -2,6 +2,8 @@
import pytest
import osxphotos
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"
@ -13,37 +15,30 @@ UUID_DICT = {
}
def test_incloud():
import osxphotos
@pytest.fixture(scope="module")
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"]])
assert photos[0].incloud
def test_not_incloud():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_not_incloud(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
assert not photos[0].incloud
def test_cloudasset_1():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_cloudasset_1(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
assert photos[0].iscloudasset
def test_cloudasset_2():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_cloudasset_2(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
# not_incloud is still a cloud asset

View File

@ -2,6 +2,8 @@
import pytest
import osxphotos
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"
@ -13,37 +15,30 @@ UUID_DICT = {
}
def test_incloud():
import osxphotos
@pytest.fixture(scope="module")
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"]])
assert photos[0].incloud
def test_not_incloud():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_not_incloud(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
assert not photos[0].incloud
def test_cloudasset_1():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_cloudasset_1(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
assert photos[0].iscloudasset
def test_cloudasset_2():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_cloudasset_2(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
# not_incloud is still a cloud asset

View File

@ -2,6 +2,8 @@
import pytest
import osxphotos
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"
@ -13,37 +15,29 @@ UUID_DICT = {
}
def test_incloud():
import osxphotos
@pytest.fixture(scope="module")
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"]])
assert photos[0].incloud
def test_not_incloud():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_not_incloud(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
assert not photos[0].incloud
def test_cloudasset_1():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_cloudasset_1(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
assert photos[0].iscloudasset
def test_cloudasset_2():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
def test_cloudasset_2(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
# not_incloud is still a cloud asset

View File

@ -2,6 +2,8 @@
import pytest
import osxphotos
PHOTOS_DB = "./tests/Test-Cloud-10.15.1.photoslibrary/database/photos.db"
UUID_DICT = {
@ -10,38 +12,34 @@ UUID_DICT = {
}
def test_live_photo():
import osxphotos
@pytest.fixture(scope="module")
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"]])
assert photos[0].live_photo
assert photos[0].path_live_photo is not None
def test_not_live_photo():
import osxphotos
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
def test_not_live_photo(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_live"]])
assert not photos[0].live_photo
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
import glob
import os.path
import pathlib
import tempfile
import osxphotos
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
filename = photos[0].original_filename
@ -56,18 +54,15 @@ def test_export_live_1():
assert got_movie in files
def test_export_live_2():
def test_export_live_2(photosdb):
# don't export the live photo
import glob
import os.path
import pathlib
import tempfile
import osxphotos
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
filename = photos[0].original_filename
@ -82,7 +77,7 @@ def test_export_live_2():
assert got_movie not in files
def test_export_live_3():
def test_export_live_3(photosdb):
# export a live photo and associated .mov,
# check list return of export
import glob
@ -90,11 +85,8 @@ def test_export_live_3():
import pathlib
import tempfile
import osxphotos
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
filename = photos[0].original_filename

View File

@ -1,5 +1,7 @@
import pytest
import osxphotos
PHOTOS_DB = "./tests/Test-10.15.7.photoslibrary/database/photos.db"
UUID_DICT = {
@ -7,12 +9,13 @@ UUID_DICT = {
"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 osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["modified"]])
assert photos[0].date_modified is not None
assert photos[0].date_modified == datetime.datetime(
@ -27,9 +30,6 @@ def test_modified():
)
def test_not_modified():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
def test_not_modified(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_modified"]])
assert photos[0].date_modified is None

View File

@ -1,5 +1,7 @@
import pytest
import osxphotos
PHOTOS_DB = "./tests/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"
@ -9,12 +11,12 @@ UUID_DICT = {
"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"]])
assert photos[0].date_modified is not None
assert photos[0].date_modified.isoformat() == "2019-12-01T11:43:45.714123-04:00"

View File

@ -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"

View File

@ -356,7 +356,7 @@ def photosdb_cloud():
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
from osxphotos.phototemplate import TEMPLATE_SUBSTITUTIONS, PhotoTemplate
@ -370,7 +370,7 @@ def test_lookup(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 re
from osxphotos.phototemplate import (
@ -390,7 +390,7 @@ def test_lookup_multi(photosdb_places):
def test_subst(photosdb_places):
""" Test that substitutions are correct """
"""Test that substitutions are correct"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
@ -402,7 +402,7 @@ def test_subst(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
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):
""" 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
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):
""" Test that substitutions are correct in user locale"""
"""Test that substitutions are correct in user locale"""
import locale
# 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):
""" Test that substitutions are correct in user locale"""
"""Test that substitutions are correct in user locale"""
import locale
import os
@ -462,7 +462,7 @@ def test_subst_locale_2(photosdb_places):
def test_subst_default_val(photosdb_places):
""" Test substitution with default value specified """
"""Test substitution with default value specified"""
import locale
import osxphotos
@ -475,7 +475,7 @@ def test_subst_default_val(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
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):
""" Test substitution with unknown value specified """
"""Test substitution with unknown value specified"""
import locale
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):
""" Test substitution with unknown value specified """
"""Test substitution with unknown value specified"""
import locale
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):
""" Test that substitutions are correct """
"""Test that substitutions are correct"""
# one album, one keyword, two persons
import osxphotos
@ -537,20 +537,24 @@ def test_subst_multi_1_1_2(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
# one album, one keyword, two persons
photo = photosdb.photos(uuid=[UUID_DICT["2_1_1"]])[0]
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)
assert sorted(rendered) == sorted(expected)
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
# 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):
""" Test that substitutions are correct """
"""Test that substitutions are correct"""
# 0 albums, 2 keywords, 0 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):
""" Test that substitutions are correct """
"""Test that substitutions are correct"""
# 0 albums, 2 keywords, 0 persons, but only do albums
# 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):
""" Test that substitutions are correct """
"""Test that substitutions are correct"""
# 0 albums, 2 keywords, 0 persons, default vals provided
# 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):
""" Test that substitutions are correct """
"""Test that substitutions are correct"""
# 0 albums, 2 keywords, 0 persons, default vals provided, unknown val in template
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):
""" Test that substitutions are correct """
"""Test that substitutions are correct"""
# 0 albums, 2 keywords, 0 persons, default vals provided, unknown val in template
# 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):
""" Test substitutions for folder_album are correct """
"""Test substitutions for folder_album are correct"""
# photo in an album in a folder
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):
""" 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 = 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):
""" 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 = 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):
""" Test substitutions for folder_album are correct """
"""Test substitutions for folder_album are correct"""
# photo in an album in a folder
photo = photosdb.photos(uuid=[UUID_DICT["folder_album_no_folder"]])[0]
template = "{folder_album}"
expected = ["Pumpkin Farm", "Test Album"]
expected = ["Multi Keyword", "Pumpkin Farm", "Test Album"]
rendered, unknown = photo.render_template(template)
assert sorted(rendered) == sorted(expected)
assert unknown == []
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 = photosdb.photos(uuid=[UUID_DICT["folder_album_no_folder"]])[0]
template = "{folder_album(:)}"
expected = ["Pumpkin Farm", "Test Album"]
expected = ["Multi Keyword", "Pumpkin Farm", "Test Album"]
rendered, unknown = photo.render_template(template)
assert sorted(rendered) == sorted(expected)
assert unknown == []
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 = 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):
""" 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
# 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):
""" 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
# 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):
""" Test that strftime substitutions are correct """
"""Test that strftime substitutions are correct"""
import locale
import osxphotos
@ -762,7 +766,7 @@ def test_subst_strftime(photosdb_places):
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
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):
""" Test that substitutions are correct when expand_inplace=True """
"""Test that substitutions are correct when expand_inplace=True"""
# one album, one keyword, two persons
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):
""" 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
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):
""" test {media_type} template """
"""test {media_type} template"""
for field, uuid in UUID_MEDIA_TYPE.items():
if uuid is not None:
@ -817,7 +821,7 @@ def test_media_type(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():
if uuid is not None:
@ -827,7 +831,7 @@ def test_media_type_default(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():
if uuid is not None:
@ -840,7 +844,7 @@ def test_bool_values(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():
if uuid is not None:
@ -850,7 +854,7 @@ def test_bool_values_not(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:
photo = photosdb_cloud.get_photo(uuid)
@ -865,7 +869,7 @@ def test_partial_match(photosdb_cloud):
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
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):
""" 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)
@ -895,7 +899,7 @@ def test_exiftool_template(photosdb):
def test_hdr(photosdb):
""" Test hdr """
"""Test hdr"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
photomock = PhotoInfoMock(photo, hdr="hdr")
rendered, _ = photomock.render_template("{hdr}")
@ -903,7 +907,7 @@ def test_hdr(photosdb):
def test_edited(photosdb):
""" Test edited """
"""Test edited"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
photomock = PhotoInfoMock(photo, hasadjustments=True)
rendered, _ = photomock.render_template("{edited}")
@ -911,7 +915,7 @@ def test_edited(photosdb):
def test_favorite(photosdb):
""" Test favorite"""
"""Test favorite"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
photomock = PhotoInfoMock(photo, favorite=True)
rendered, _ = photomock.render_template("{favorite}")
@ -973,14 +977,14 @@ def test_conditional(photosdb):
def test_function(photosdb):
""" Test {function} """
"""Test {function}"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
rendered, _ = photo.render_template("{function:tests/template_function.py::foo}")
assert rendered == [f"{photo.original_filename}-FOO"]
def test_function_bad(photosdb):
""" Test invalid {function} """
"""Test invalid {function}"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
with pytest.raises(ValueError):
rendered, _ = photo.render_template(
@ -989,7 +993,7 @@ def test_function_bad(photosdb):
def test_function_filter(photosdb):
""" Test {field|function} filter"""
"""Test {field|function} filter"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
rendered, _ = photo.render_template(
@ -1009,7 +1013,7 @@ def test_function_filter(photosdb):
def test_function_filter_bad(photosdb):
""" Test invalid {field|function} filter"""
"""Test invalid {field|function} filter"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
with pytest.raises(ValueError):
rendered, _ = photo.render_template(