Cleaned up tests, fixed bug in PhotosDB.query
This commit is contained in:
parent
4b6c35b5f9
commit
0758f84dc4
@ -1497,7 +1497,7 @@ Substitution Description
|
||||
{lf} A line feed: '\n', alias for {newline}
|
||||
{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|
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,13 +78,13 @@ 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.
|
||||
verbose: optional callable function to use for printing verbose text during processing; if None (default), does not print output.
|
||||
exiftool: optional path to exiftool for methods that require this (e.g. PhotoInfo.exiftool); if not provided, will search PATH
|
||||
|
||||
|
||||
Raises:
|
||||
FileNotFoundError if dbfile is not a valid Photos library.
|
||||
TypeError if verbose is not None and not callable.
|
||||
@ -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,19 +2624,19 @@ 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
|
||||
|
||||
|
||||
Args:
|
||||
shared: boolean; if True, returns shared albums, else normal albums
|
||||
import_session: boolean, if True, returns import session albums, else normal or shared albums
|
||||
Note: flags (shared, import_session) are mutually exclusive
|
||||
|
||||
|
||||
Raises:
|
||||
ValueError: raised if mutually exclusive flags passed
|
||||
|
||||
Returns: list of album UUIDs
|
||||
Returns: list of album UUIDs
|
||||
"""
|
||||
if shared and import_session:
|
||||
raise ValueError(
|
||||
@ -2688,14 +2688,14 @@ 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
|
||||
|
||||
Args:
|
||||
shared: boolean; if True, returns shared albums, else normal albums
|
||||
|
||||
|
||||
Returns: list of album names
|
||||
"""
|
||||
|
||||
@ -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)
|
||||
@ -2729,10 +2729,10 @@ class PhotosDB:
|
||||
persons: list of persons to search for
|
||||
albums: list of album names to search for
|
||||
images: if True, returns image files, if False, does not return images; default is True
|
||||
movies: if True, returns movie files, if False, does not return movies; default is True
|
||||
movies: if True, returns movie files, if False, does not return movies; default is True
|
||||
from_date: return photos with creation date >= from_date (datetime.datetime object, default None)
|
||||
to_date: return photos with creation date <= to_date (datetime.datetime object, default None)
|
||||
intrash: if True, returns only images in "Recently deleted items" folder,
|
||||
intrash: if True, returns only images in "Recently deleted items" folder,
|
||||
if False returns only photos that aren't deleted; default is False
|
||||
|
||||
Returns:
|
||||
@ -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))
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -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>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:Subject": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:TagsList": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:PersonInImage": ["Katie"], "EXIF:GPSLatitude": 41.256566, "EXIF:GPSLongitude": -95.940257, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:Subject": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:TagsList": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:PersonInImage": ["Katie"], "EXIF:GPSLatitude": 41.256566, "EXIF:GPSLongitude": -95.940257, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
@ -18,6 +18,7 @@
|
||||
<dc:subject>
|
||||
<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>
|
||||
|
||||
@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:Subject": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:TagsList": ["Kids", "Pumpkin Farm", "Test Album"], "XMP:PersonInImage": ["Katie"], "EXIF:GPSLatitude": 41.256566, "EXIF:GPSLongitude": -95.940257, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:Subject": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:TagsList": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"], "XMP:PersonInImage": ["Katie"], "EXIF:GPSLatitude": 41.256566, "EXIF:GPSLongitude": -95.940257, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
@ -19,6 +19,7 @@
|
||||
<rdf:Bag>
|
||||
<rdf: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>
|
||||
|
||||
@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", "IPTC:Caption-Abstract": "Bride Wedding day", "IPTC:Keywords": ["AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:Subject": ["AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:TagsList": ["AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:PersonInImage": ["Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:CreateDate": "2019:04:15 14:40:24", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2019:04:15", "IPTC:TimeCreated": "14:40:24-04:00", "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
|
||||
[{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", "IPTC:Caption-Abstract": "Bride Wedding day", "IPTC:Keywords": ["AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:Subject": ["AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:TagsList": ["AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:PersonInImage": ["Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:CreateDate": "2019:04:15 14:40:24", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2019:04:15", "IPTC:TimeCreated": "14:40:24-04:00", "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
|
||||
@ -20,6 +20,7 @@
|
||||
<rdf:li>AlbumInFolder</rdf:li>
|
||||
<rdf:li>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>
|
||||
|
||||
@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", "IPTC:Caption-Abstract": "Bride Wedding day", "IPTC:Keywords": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:Subject": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:TagsList": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "wedding"], "XMP:PersonInImage": ["Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:CreateDate": "2019:04:15 14:40:24", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2019:04:15", "IPTC:TimeCreated": "14:40:24-04:00", "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
|
||||
[{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", "IPTC:Caption-Abstract": "Bride Wedding day", "IPTC:Keywords": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:Subject": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:TagsList": ["Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin", "Maria", "Multi Keyword", "wedding"], "XMP:PersonInImage": ["Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:CreateDate": "2019:04:15 14:40:24", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2019:04:15", "IPTC:TimeCreated": "14:40:24-04:00", "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
|
||||
@ -21,6 +21,7 @@
|
||||
<rdf:li>Folder1/SubFolder2/AlbumInFolder</rdf:li>
|
||||
<rdf:li>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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
@ -1,830 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from osxphotos._constants import _UNKNOWN_PERSON
|
||||
|
||||
# TODO: put some of this code into a pre-function
|
||||
|
||||
PHOTOS_DB = "./tests/Test-10.15.1.photoslibrary/database/photos.db"
|
||||
PHOTOS_DB_PATH = "/Test-10.15.1.photoslibrary/database/photos.db"
|
||||
PHOTOS_LIBRARY_PATH = "/Test-10.15.1.photoslibrary"
|
||||
|
||||
KEYWORDS = [
|
||||
"Kids",
|
||||
"wedding",
|
||||
"flowers",
|
||||
"England",
|
||||
"London",
|
||||
"London 2018",
|
||||
"St. James's Park",
|
||||
"UK",
|
||||
"United Kingdom",
|
||||
]
|
||||
# Photos 5 includes blank person for detected face
|
||||
PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON]
|
||||
ALBUMS = [
|
||||
"Pumpkin Farm",
|
||||
"Test Album",
|
||||
"Multi Keyword",
|
||||
] # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
||||
KEYWORDS_DICT = {
|
||||
"Kids": 4,
|
||||
"wedding": 2,
|
||||
"flowers": 1,
|
||||
"England": 1,
|
||||
"London": 1,
|
||||
"London 2018": 1,
|
||||
"St. James's Park": 1,
|
||||
"UK": 1,
|
||||
"United Kingdom": 1,
|
||||
}
|
||||
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1, _UNKNOWN_PERSON: 1}
|
||||
ALBUM_DICT = {
|
||||
"Pumpkin Farm": 3,
|
||||
"Test Album": 2,
|
||||
"Multi Keyword": 2,
|
||||
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
||||
|
||||
UUID_DICT = {
|
||||
"missing": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
||||
"favorite": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
"not_favorite": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
||||
"hidden": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
||||
"not_hidden": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
"has_adjustments": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
"no_adjustments": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
||||
"location": "DC99FBDD-7A52-4100-A5BB-344131646C30",
|
||||
"no_location": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
||||
"external_edit": "DC99FBDD-7A52-4100-A5BB-344131646C30",
|
||||
"no_external_edit": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
"export": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
|
||||
"multi_query_1": "D79B8D77-BFFC-460B-9312-034F2877D35B",
|
||||
"multi_query_2": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
}
|
||||
|
||||
|
||||
def test_init1():
|
||||
# test named argument
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
||||
|
||||
|
||||
def test_init2():
|
||||
# test positional argument
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
||||
|
||||
|
||||
def test_init3():
|
||||
# test positional and named argument (raises exception)
|
||||
import osxphotos
|
||||
|
||||
with pytest.raises(Exception):
|
||||
assert osxphotos.PhotosDB(PHOTOS_DB, dbfile=PHOTOS_DB)
|
||||
|
||||
|
||||
def test_init4():
|
||||
# test invalid db
|
||||
import os
|
||||
import tempfile
|
||||
import osxphotos
|
||||
|
||||
(bad_db, bad_db_name) = tempfile.mkstemp(suffix=".db", prefix="osxphotos-")
|
||||
os.close(bad_db)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
assert osxphotos.PhotosDB(bad_db_name)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
assert osxphotos.PhotosDB(dbfile=bad_db_name)
|
||||
|
||||
try:
|
||||
os.remove(bad_db_name)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def test_init5(mocker):
|
||||
# test failed get_last_library_path
|
||||
import osxphotos
|
||||
|
||||
def bad_library():
|
||||
return None
|
||||
|
||||
# get_last_library actually in utils but need to patch it in photosdb because it's imported into photosdb
|
||||
# because of the layout of photosdb/ need to patch it this way...don't really understand why, but it works
|
||||
mocker.patch("osxphotos.photosdb.photosdb.get_last_library_path", new=bad_library)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
assert osxphotos.PhotosDB()
|
||||
|
||||
|
||||
def test_db_version():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
||||
assert photosdb.db_version == "6000"
|
||||
|
||||
|
||||
def test_persons():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "Katie" in photosdb.persons
|
||||
assert collections.Counter(PERSONS) == collections.Counter(photosdb.persons)
|
||||
|
||||
|
||||
def test_keywords():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "wedding" in photosdb.keywords
|
||||
assert collections.Counter(KEYWORDS) == collections.Counter(photosdb.keywords)
|
||||
|
||||
|
||||
def test_album_names():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "Pumpkin Farm" in photosdb.albums
|
||||
assert collections.Counter(ALBUMS) == collections.Counter(photosdb.albums)
|
||||
|
||||
|
||||
def test_keywords_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
keywords = photosdb.keywords_as_dict
|
||||
assert keywords["wedding"] == 2
|
||||
assert keywords == KEYWORDS_DICT
|
||||
|
||||
|
||||
def test_persons_as_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
persons = photosdb.persons_as_dict
|
||||
assert persons["Maria"] == 1
|
||||
assert persons == PERSONS_DICT
|
||||
|
||||
|
||||
def test_albums_as_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
albums = photosdb.albums_as_dict
|
||||
assert albums["Pumpkin Farm"] == 3
|
||||
assert albums == ALBUM_DICT
|
||||
|
||||
|
||||
def test_attributes():
|
||||
import datetime
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["D79B8D77-BFFC-460B-9312-034F2877D35B"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.keywords == ["Kids"]
|
||||
assert p.original_filename == "Pumkins2.jpg"
|
||||
assert p.filename == "D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
||||
assert p.date == datetime.datetime(
|
||||
2018, 9, 28, 16, 7, 7, 0, datetime.timezone(datetime.timedelta(seconds=-14400))
|
||||
)
|
||||
assert p.description == "Girl holding pumpkin"
|
||||
assert p.title == "I found one!"
|
||||
assert sorted(p.albums) == ["Multi Keyword", "Pumpkin Farm", "Test Album"]
|
||||
assert p.persons == ["Katie"]
|
||||
assert p.path.endswith(
|
||||
"tests/Test-10.15.1.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
||||
)
|
||||
assert p.ismissing == False
|
||||
|
||||
|
||||
def test_missing():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.path == None
|
||||
assert p.ismissing == True
|
||||
|
||||
|
||||
def test_favorite():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.favorite == True
|
||||
|
||||
|
||||
def test_not_favorite():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["not_favorite"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.favorite == False
|
||||
|
||||
|
||||
def test_hidden():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["hidden"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.hidden == True
|
||||
|
||||
|
||||
def test_not_hidden():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.hidden == False
|
||||
|
||||
|
||||
def test_location_1():
|
||||
# test photo with lat/lon info
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["location"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
lat, lon = p.location
|
||||
assert lat == pytest.approx(51.50357167)
|
||||
assert lon == pytest.approx(-0.1318055)
|
||||
|
||||
|
||||
def test_location_2():
|
||||
# test photo with no location info
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_location"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
lat, lon = p.location
|
||||
assert lat is None
|
||||
assert lon is None
|
||||
|
||||
|
||||
def test_hasadjustments1():
|
||||
# test hasadjustments == True
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.hasadjustments == True
|
||||
|
||||
|
||||
def test_hasadjustments2():
|
||||
# test hasadjustments == False
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.hasadjustments == False
|
||||
|
||||
|
||||
def test_external_edit1():
|
||||
# test image has been edited in external editor
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["external_edit"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
|
||||
assert p.external_edit == True
|
||||
|
||||
|
||||
def test_external_edit2():
|
||||
# test image has not been edited in external editor
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_external_edit"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
|
||||
assert p.external_edit == False
|
||||
|
||||
|
||||
def test_path_edited1():
|
||||
# test a valid edited path
|
||||
import os.path
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
path = p.path_edited
|
||||
assert path.endswith(
|
||||
"resources/renders/E/E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51_1_201_a.jpeg"
|
||||
)
|
||||
assert os.path.exists(path)
|
||||
|
||||
|
||||
def test_path_edited2():
|
||||
# test an invalid edited path
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["6191423D-8DB8-4D4C-92BE-9BBBA308AAC4"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
path = p.path_edited
|
||||
assert path is None
|
||||
|
||||
|
||||
def test_count():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos()
|
||||
assert len(photos) == 7
|
||||
|
||||
|
||||
def test_keyword_2():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(keywords=["wedding"])
|
||||
assert len(photos) == 2
|
||||
|
||||
|
||||
def test_keyword_not_in_album():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
|
||||
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
|
||||
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
|
||||
photos2 = photosdb.photos(keywords=["Kids"])
|
||||
photos3 = [p for p in photos2 if p not in photos1]
|
||||
assert len(photos3) == 1
|
||||
assert photos3[0].uuid == "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C"
|
||||
|
||||
|
||||
def test_get_db_path():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
db_path = photosdb.db_path
|
||||
assert db_path.endswith(PHOTOS_DB_PATH)
|
||||
|
||||
|
||||
def test_get_library_path():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
lib_path = photosdb.library_path
|
||||
assert lib_path.endswith(PHOTOS_LIBRARY_PATH)
|
||||
|
||||
|
||||
def test_export_1():
|
||||
# test basic export
|
||||
# get an unedited image and export it using default filename
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].original_filename
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
got_dest = photos[0].export(dest)[0]
|
||||
|
||||
assert got_dest == expected_dest
|
||||
assert os.path.isfile(got_dest)
|
||||
|
||||
|
||||
def test_export_2():
|
||||
# test export with user provided filename
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
got_dest = photos[0].export(dest, filename)[0]
|
||||
|
||||
assert got_dest == expected_dest
|
||||
assert os.path.isfile(got_dest)
|
||||
|
||||
|
||||
def test_export_3():
|
||||
# test file already exists and test increment=True (default)
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].original_filename
|
||||
filename2 = pathlib.Path(filename)
|
||||
filename2 = f"{filename2.stem} (1){filename2.suffix}"
|
||||
expected_dest_2 = os.path.join(dest, filename2)
|
||||
|
||||
got_dest = photos[0].export(dest)[0]
|
||||
got_dest_2 = photos[0].export(dest)[0]
|
||||
|
||||
assert got_dest_2 == expected_dest_2
|
||||
assert os.path.isfile(got_dest_2)
|
||||
|
||||
|
||||
def test_export_4():
|
||||
# test user supplied file already exists and test increment=True (default)
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
|
||||
filename2 = f"osxphotos-export-2-test-{timestamp} (1).jpg"
|
||||
expected_dest_2 = os.path.join(dest, filename2)
|
||||
|
||||
got_dest = photos[0].export(dest, filename)[0]
|
||||
got_dest_2 = photos[0].export(dest, filename)[0]
|
||||
|
||||
assert got_dest_2 == expected_dest_2
|
||||
assert os.path.isfile(got_dest_2)
|
||||
|
||||
|
||||
def test_export_5():
|
||||
# test file already exists and test increment=True (default)
|
||||
# and overwrite = True
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].original_filename
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
|
||||
got_dest = photos[0].export(dest)[0]
|
||||
got_dest_2 = photos[0].export(dest, overwrite=True)[0]
|
||||
|
||||
assert got_dest_2 == got_dest
|
||||
assert got_dest_2 == expected_dest
|
||||
assert os.path.isfile(got_dest_2)
|
||||
|
||||
|
||||
def test_export_6():
|
||||
# test user supplied file already exists and test increment=True (default)
|
||||
# and overwrite = True
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
|
||||
got_dest = photos[0].export(dest, filename)[0]
|
||||
got_dest_2 = photos[0].export(dest, filename, overwrite=True)[0]
|
||||
|
||||
assert got_dest_2 == got_dest
|
||||
assert got_dest_2 == expected_dest
|
||||
assert os.path.isfile(got_dest_2)
|
||||
|
||||
|
||||
def test_export_7():
|
||||
# test file already exists and test increment=False (not default), overwrite=False (default)
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].filename
|
||||
|
||||
got_dest = photos[0].export(dest)[0]
|
||||
with pytest.raises(Exception) as e:
|
||||
# try to export again with increment = False
|
||||
assert photos[0].export(dest, increment=False)
|
||||
assert e.type == type(FileExistsError())
|
||||
|
||||
|
||||
def test_export_8():
|
||||
# try to export missing file
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
||||
|
||||
filename = photos[0].filename
|
||||
|
||||
with pytest.raises(Exception) as e:
|
||||
assert photos[0].export(dest)[0]
|
||||
assert e.type == type(FileNotFoundError())
|
||||
|
||||
|
||||
def test_export_9():
|
||||
# try to export edited file that's not edited
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
||||
|
||||
filename = photos[0].filename
|
||||
|
||||
with pytest.raises(Exception) as e:
|
||||
assert photos[0].export(dest, edited=True)
|
||||
assert e.type == ValueError
|
||||
|
||||
|
||||
def test_export_10():
|
||||
# try to export edited file that's not edited and name provided
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
||||
|
||||
with pytest.raises(Exception) as e:
|
||||
assert photos[0].export(dest, filename, edited=True)
|
||||
assert e.type == ValueError
|
||||
|
||||
|
||||
def test_export_11():
|
||||
# export edited file with name provided
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
|
||||
got_dest = photos[0].export(dest, filename, edited=True)[0]
|
||||
assert got_dest == expected_dest
|
||||
|
||||
|
||||
def test_export_12():
|
||||
# export edited file with default name
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
||||
|
||||
edited_name = pathlib.Path(photos[0].path_edited).name
|
||||
edited_suffix = pathlib.Path(edited_name).suffix
|
||||
filename = pathlib.Path(photos[0].original_filename).stem + "_edited" + edited_suffix
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
|
||||
got_dest = photos[0].export(dest, edited=True)[0]
|
||||
assert got_dest == expected_dest
|
||||
|
||||
|
||||
def test_export_13():
|
||||
# export to invalid destination
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
|
||||
# create a folder that doesn't exist
|
||||
i = 0
|
||||
while os.path.isdir(dest):
|
||||
dest = os.path.join(dest, str(i))
|
||||
i += 1
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].filename
|
||||
|
||||
with pytest.raises(Exception) as e:
|
||||
assert photos[0].export(dest)
|
||||
assert e.type == type(FileNotFoundError())
|
||||
|
||||
|
||||
def test_eq():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
photos2 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
assert photos1[0] == photos2[0]
|
||||
|
||||
|
||||
def test_not_eq():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
photos2 = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
||||
assert photos1[0] != photos2[0]
|
||||
|
||||
|
||||
def test_photosdb_repr():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photosdb2 = eval(repr(photosdb))
|
||||
|
||||
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name"]
|
||||
assert {k: v for k, v in photosdb.__dict__.items() if k not in ignore_keys} == {
|
||||
k: v for k, v in photosdb2.__dict__.items() if k not in ignore_keys
|
||||
}
|
||||
|
||||
|
||||
def test_photosinfo_repr():
|
||||
import osxphotos
|
||||
import datetime
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
|
||||
photo = photos[0]
|
||||
photo2 = eval(repr(photo))
|
||||
|
||||
assert {k: str(v).encode("utf-8") for k, v in photo.__dict__.items()} == {
|
||||
k: str(v).encode("utf-8") for k, v in photo2.__dict__.items()
|
||||
}
|
||||
|
||||
|
||||
def test_from_to_date():
|
||||
import osxphotos
|
||||
import datetime as dt
|
||||
import os
|
||||
import time
|
||||
|
||||
os.environ["TZ"] = "US/Pacific"
|
||||
time.tzset()
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
|
||||
photos = photosdb.photos(from_date=dt.datetime(2018, 10, 28))
|
||||
assert len(photos) == 2
|
||||
|
||||
photos = photosdb.photos(to_date=dt.datetime(2018, 10, 28))
|
||||
assert len(photos) == 5
|
||||
|
||||
photos = photosdb.photos(
|
||||
from_date=dt.datetime(2018, 9, 28), to_date=dt.datetime(2018, 9, 29)
|
||||
)
|
||||
assert len(photos) == 4
|
||||
|
||||
|
||||
def test_multi_uuid():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"], UUID_DICT["not_favorite"]])
|
||||
|
||||
assert len(photos) == 2
|
||||
|
||||
|
||||
def test_multi_keyword():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
photos = photosdb.photos(keywords=["Kids", "wedding"])
|
||||
|
||||
assert len(photos) == 6
|
||||
|
||||
|
||||
def test_multi_album():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
photos = photosdb.photos(albums=["Pumpkin Farm", "Test Album"])
|
||||
|
||||
assert len(photos) == 3
|
||||
|
||||
|
||||
def test_multi_person():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
photos = photosdb.photos(persons=["Katie", "Suzy"])
|
||||
|
||||
assert len(photos) == 3
|
||||
|
||||
|
||||
def test_compound_query():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
photos = photosdb.photos(persons=["Katie", "Maria"], albums=["Multi Keyword"])
|
||||
|
||||
assert len(photos) == 2
|
||||
assert UUID_DICT["multi_query_1"] in [p.uuid for p in photos]
|
||||
assert UUID_DICT["multi_query_2"] in [p.uuid for p in photos]
|
||||
@ -1,791 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from osxphotos._constants import _UNKNOWN_PERSON
|
||||
|
||||
# TODO: put some of this code into a pre-function
|
||||
|
||||
PHOTOS_DB = "./tests/Test-10.15.4.photoslibrary/database/photos.db"
|
||||
PHOTOS_DB_PATH = "/Test-10.15.4.photoslibrary/database/photos.db"
|
||||
PHOTOS_LIBRARY_PATH = "/Test-10.15.4.photoslibrary"
|
||||
|
||||
KEYWORDS = [
|
||||
"Kids",
|
||||
"wedding",
|
||||
"flowers",
|
||||
"England",
|
||||
"London",
|
||||
"London 2018",
|
||||
"St. James's Park",
|
||||
"UK",
|
||||
"United Kingdom",
|
||||
]
|
||||
# Photos 5 includes blank person for detected face
|
||||
PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON]
|
||||
ALBUMS = [
|
||||
"Pumpkin Farm",
|
||||
"Test Album",
|
||||
"AlbumInFolder",
|
||||
"Raw",
|
||||
] # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
||||
KEYWORDS_DICT = {
|
||||
"Kids": 4,
|
||||
"wedding": 2,
|
||||
"flowers": 1,
|
||||
"England": 1,
|
||||
"London": 1,
|
||||
"London 2018": 1,
|
||||
"St. James's Park": 1,
|
||||
"UK": 1,
|
||||
"United Kingdom": 1,
|
||||
}
|
||||
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1, _UNKNOWN_PERSON: 1}
|
||||
ALBUM_DICT = {
|
||||
"Pumpkin Farm": 3,
|
||||
"Test Album": 2,
|
||||
"AlbumInFolder": 2,
|
||||
"Raw": 4,
|
||||
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
||||
|
||||
UUID_DICT = {
|
||||
"missing": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
||||
"favorite": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
"not_favorite": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
||||
"hidden": "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C",
|
||||
"not_hidden": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
"has_adjustments": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
"no_adjustments": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
||||
"location": "DC99FBDD-7A52-4100-A5BB-344131646C30",
|
||||
"no_location": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
||||
"external_edit": "DC99FBDD-7A52-4100-A5BB-344131646C30",
|
||||
"no_external_edit": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||
"export": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
|
||||
}
|
||||
|
||||
|
||||
def test_init1():
|
||||
# test named argument
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
||||
|
||||
|
||||
def test_init2():
|
||||
# test positional argument
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
||||
|
||||
|
||||
def test_init3():
|
||||
# test positional and named argument (raises exception)
|
||||
import osxphotos
|
||||
|
||||
with pytest.raises(Exception):
|
||||
assert osxphotos.PhotosDB(PHOTOS_DB, dbfile=PHOTOS_DB)
|
||||
|
||||
|
||||
def test_init4():
|
||||
# test invalid db
|
||||
import os
|
||||
import tempfile
|
||||
import osxphotos
|
||||
|
||||
(bad_db, bad_db_name) = tempfile.mkstemp(suffix=".db", prefix="osxphotos-")
|
||||
os.close(bad_db)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
assert osxphotos.PhotosDB(bad_db_name)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
assert osxphotos.PhotosDB(dbfile=bad_db_name)
|
||||
|
||||
try:
|
||||
os.remove(bad_db_name)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def test_init5(mocker):
|
||||
# test failed get_last_library_path
|
||||
import osxphotos
|
||||
|
||||
def bad_library():
|
||||
return None
|
||||
|
||||
# get_last_library actually in utils but need to patch it in photosdb because it's imported into photosdb
|
||||
# because of the layout of photosdb/ need to patch it this way...don't really understand why, but it works
|
||||
mocker.patch("osxphotos.photosdb.photosdb.get_last_library_path", new=bad_library)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
assert osxphotos.PhotosDB()
|
||||
|
||||
|
||||
def test_db_len():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
||||
assert len(photosdb) == 12
|
||||
|
||||
|
||||
def test_db_version():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
||||
assert photosdb.db_version == "6000"
|
||||
|
||||
|
||||
def test_persons():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "Katie" in photosdb.persons
|
||||
assert collections.Counter(PERSONS) == collections.Counter(photosdb.persons)
|
||||
|
||||
|
||||
def test_keywords():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "wedding" in photosdb.keywords
|
||||
assert collections.Counter(KEYWORDS) == collections.Counter(photosdb.keywords)
|
||||
|
||||
|
||||
def test_album_names():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "Pumpkin Farm" in photosdb.albums
|
||||
assert collections.Counter(ALBUMS) == collections.Counter(photosdb.albums)
|
||||
|
||||
|
||||
def test_keywords_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
keywords = photosdb.keywords_as_dict
|
||||
assert keywords["wedding"] == 2
|
||||
assert keywords == KEYWORDS_DICT
|
||||
|
||||
|
||||
def test_persons_as_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
persons = photosdb.persons_as_dict
|
||||
assert persons["Maria"] == 1
|
||||
assert persons == PERSONS_DICT
|
||||
|
||||
|
||||
def test_albums_as_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
albums = photosdb.albums_as_dict
|
||||
assert albums["Pumpkin Farm"] == 3
|
||||
assert albums == ALBUM_DICT
|
||||
|
||||
|
||||
def test_attributes():
|
||||
import datetime
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["D79B8D77-BFFC-460B-9312-034F2877D35B"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.keywords == ["Kids"]
|
||||
assert p.original_filename == "Pumkins2.jpg"
|
||||
assert p.filename == "D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
||||
assert p.date == datetime.datetime(
|
||||
2018, 9, 28, 16, 7, 7, 0, datetime.timezone(datetime.timedelta(seconds=-14400))
|
||||
)
|
||||
assert p.description == "Girl holding pumpkin"
|
||||
assert p.title == "I found one!"
|
||||
assert sorted(p.albums) == ["Pumpkin Farm", "Test Album"]
|
||||
assert p.persons == ["Katie"]
|
||||
assert p.path.endswith(
|
||||
"tests/Test-10.15.4.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
||||
)
|
||||
assert p.ismissing == False
|
||||
|
||||
|
||||
def test_missing():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.path == None
|
||||
assert p.ismissing == True
|
||||
|
||||
|
||||
def test_favorite():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.favorite == True
|
||||
|
||||
|
||||
def test_not_favorite():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["not_favorite"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.favorite == False
|
||||
|
||||
|
||||
def test_hidden():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["hidden"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.hidden == True
|
||||
|
||||
|
||||
def test_not_hidden():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.hidden == False
|
||||
|
||||
|
||||
def test_location_1():
|
||||
# test photo with lat/lon info
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["location"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
lat, lon = p.location
|
||||
assert lat == pytest.approx(51.50357167)
|
||||
assert lon == pytest.approx(-0.1318055)
|
||||
|
||||
|
||||
def test_location_2():
|
||||
# test photo with no location info
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_location"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
lat, lon = p.location
|
||||
assert lat is None
|
||||
assert lon is None
|
||||
|
||||
|
||||
def test_hasadjustments1():
|
||||
# test hasadjustments == True
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.hasadjustments == True
|
||||
|
||||
|
||||
def test_hasadjustments2():
|
||||
# test hasadjustments == False
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.hasadjustments == False
|
||||
|
||||
|
||||
def test_external_edit1():
|
||||
# test image has been edited in external editor
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["external_edit"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
|
||||
assert p.external_edit == True
|
||||
|
||||
|
||||
def test_external_edit2():
|
||||
# test image has not been edited in external editor
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_external_edit"]])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
|
||||
assert p.external_edit == False
|
||||
|
||||
|
||||
def test_path_edited1():
|
||||
# test a valid edited path
|
||||
import os.path
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
path = p.path_edited
|
||||
assert path.endswith(
|
||||
"resources/renders/E/E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51_1_201_a.jpeg"
|
||||
)
|
||||
assert os.path.exists(path)
|
||||
|
||||
|
||||
def test_path_edited2():
|
||||
# test an invalid edited path
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["6191423D-8DB8-4D4C-92BE-9BBBA308AAC4"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
path = p.path_edited
|
||||
assert path is None
|
||||
|
||||
|
||||
def test_count():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos()
|
||||
assert len(photos) == 12
|
||||
|
||||
|
||||
def test_keyword_2():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(keywords=["wedding"])
|
||||
assert len(photos) == 2
|
||||
|
||||
|
||||
def test_keyword_not_in_album():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
|
||||
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
|
||||
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
|
||||
photos2 = photosdb.photos(keywords=["Kids"])
|
||||
photos3 = [p for p in photos2 if p not in photos1]
|
||||
assert len(photos3) == 1
|
||||
assert photos3[0].uuid == "A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C"
|
||||
|
||||
|
||||
def test_get_db_path():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
db_path = photosdb.db_path
|
||||
assert db_path.endswith(PHOTOS_DB_PATH)
|
||||
|
||||
|
||||
def test_get_library_path():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
lib_path = photosdb.library_path
|
||||
assert lib_path.endswith(PHOTOS_LIBRARY_PATH)
|
||||
|
||||
|
||||
def test_export_1():
|
||||
# test basic export
|
||||
# get an unedited image and export it using default filename
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].original_filename
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
got_dest = photos[0].export(dest)[0]
|
||||
|
||||
assert got_dest == expected_dest
|
||||
assert os.path.isfile(got_dest)
|
||||
|
||||
|
||||
def test_export_2():
|
||||
# test export with user provided filename
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
got_dest = photos[0].export(dest, filename)[0]
|
||||
|
||||
assert got_dest == expected_dest
|
||||
assert os.path.isfile(got_dest)
|
||||
|
||||
|
||||
def test_export_3():
|
||||
# test file already exists and test increment=True (default)
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].original_filename
|
||||
filename2 = pathlib.Path(filename)
|
||||
filename2 = f"{filename2.stem} (1){filename2.suffix}"
|
||||
expected_dest_2 = os.path.join(dest, filename2)
|
||||
|
||||
got_dest = photos[0].export(dest)[0]
|
||||
got_dest_2 = photos[0].export(dest)[0]
|
||||
|
||||
assert got_dest_2 == expected_dest_2
|
||||
assert os.path.isfile(got_dest_2)
|
||||
|
||||
|
||||
def test_export_4():
|
||||
# test user supplied file already exists and test increment=True (default)
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
|
||||
filename2 = f"osxphotos-export-2-test-{timestamp} (1).jpg"
|
||||
expected_dest_2 = os.path.join(dest, filename2)
|
||||
|
||||
got_dest = photos[0].export(dest, filename)[0]
|
||||
got_dest_2 = photos[0].export(dest, filename)[0]
|
||||
|
||||
assert got_dest_2 == expected_dest_2
|
||||
assert os.path.isfile(got_dest_2)
|
||||
|
||||
|
||||
def test_export_5():
|
||||
# test file already exists and test increment=True (default)
|
||||
# and overwrite = True
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].original_filename
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
|
||||
got_dest = photos[0].export(dest)[0]
|
||||
got_dest_2 = photos[0].export(dest, overwrite=True)[0]
|
||||
|
||||
assert got_dest_2 == got_dest
|
||||
assert got_dest_2 == expected_dest
|
||||
assert os.path.isfile(got_dest_2)
|
||||
|
||||
|
||||
def test_export_6():
|
||||
# test user supplied file already exists and test increment=True (default)
|
||||
# and overwrite = True
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
|
||||
got_dest = photos[0].export(dest, filename)[0]
|
||||
got_dest_2 = photos[0].export(dest, filename, overwrite=True)[0]
|
||||
|
||||
assert got_dest_2 == got_dest
|
||||
assert got_dest_2 == expected_dest
|
||||
assert os.path.isfile(got_dest_2)
|
||||
|
||||
|
||||
def test_export_7():
|
||||
# test file already exists and test increment=False (not default), overwrite=False (default)
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].filename
|
||||
|
||||
got_dest = photos[0].export(dest)[0]
|
||||
with pytest.raises(Exception) as e:
|
||||
# try to export again with increment = False
|
||||
assert photos[0].export(dest, increment=False)
|
||||
assert e.type == type(FileExistsError())
|
||||
|
||||
|
||||
def test_export_8():
|
||||
# try to export missing file
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
||||
|
||||
filename = photos[0].filename
|
||||
|
||||
with pytest.raises(Exception) as e:
|
||||
assert photos[0].export(dest)[0]
|
||||
assert e.type == type(FileNotFoundError())
|
||||
|
||||
|
||||
def test_export_9():
|
||||
# try to export edited file that's not edited
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
||||
|
||||
filename = photos[0].filename
|
||||
|
||||
with pytest.raises(Exception) as e:
|
||||
assert photos[0].export(dest, edited=True)
|
||||
assert e.type == ValueError
|
||||
|
||||
|
||||
def test_export_10():
|
||||
# try to export edited file that's not edited and name provided
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
||||
|
||||
with pytest.raises(Exception) as e:
|
||||
assert photos[0].export(dest, filename, edited=True)
|
||||
assert e.type == ValueError
|
||||
|
||||
|
||||
def test_export_11():
|
||||
# export edited file with name provided
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
||||
|
||||
timestamp = time.time()
|
||||
filename = f"osxphotos-export-test-{timestamp}.jpg"
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
|
||||
got_dest = photos[0].export(dest, filename, edited=True)[0]
|
||||
assert got_dest == expected_dest
|
||||
|
||||
|
||||
def test_export_12():
|
||||
# export edited file with default name
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
|
||||
|
||||
edited_name = pathlib.Path(photos[0].path_edited).name
|
||||
edited_suffix = pathlib.Path(edited_name).suffix
|
||||
filename = pathlib.Path(photos[0].original_filename).stem + "_edited" + edited_suffix
|
||||
expected_dest = os.path.join(dest, filename)
|
||||
|
||||
got_dest = photos[0].export(dest, edited=True)[0]
|
||||
assert got_dest == expected_dest
|
||||
|
||||
|
||||
def test_export_13():
|
||||
# export to invalid destination
|
||||
# should raise exception
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import osxphotos
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dest = tempdir.name
|
||||
|
||||
# create a folder that doesn't exist
|
||||
i = 0
|
||||
while os.path.isdir(dest):
|
||||
dest = os.path.join(dest, str(i))
|
||||
i += 1
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
|
||||
filename = photos[0].filename
|
||||
|
||||
with pytest.raises(Exception) as e:
|
||||
assert photos[0].export(dest)
|
||||
assert e.type == type(FileNotFoundError())
|
||||
|
||||
|
||||
def test_eq():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
photos2 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
assert photos1[0] == photos2[0]
|
||||
|
||||
|
||||
def test_not_eq():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
||||
photos2 = photosdb.photos(uuid=[UUID_DICT["missing"]])
|
||||
assert photos1[0] != photos2[0]
|
||||
|
||||
|
||||
def test_photosdb_repr():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photosdb2 = eval(repr(photosdb))
|
||||
|
||||
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name"]
|
||||
assert {k: v for k, v in photosdb.__dict__.items() if k not in ignore_keys} == {
|
||||
k: v for k, v in photosdb2.__dict__.items() if k not in ignore_keys
|
||||
}
|
||||
|
||||
|
||||
def test_photosinfo_repr():
|
||||
import osxphotos
|
||||
import datetime
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
|
||||
photo = photos[0]
|
||||
photo2 = eval(repr(photo))
|
||||
|
||||
assert {k: str(v).encode("utf-8") for k, v in photo.__dict__.items()} == {
|
||||
k: str(v).encode("utf-8") for k, v in photo2.__dict__.items()
|
||||
}
|
||||
|
||||
|
||||
def test_from_to_date():
|
||||
import osxphotos
|
||||
import datetime as dt
|
||||
import os
|
||||
import time
|
||||
|
||||
os.environ["TZ"] = "US/Pacific"
|
||||
time.tzset()
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||
|
||||
photos = photosdb.photos(from_date=dt.datetime(2018, 10, 28))
|
||||
assert len(photos) == 6
|
||||
|
||||
photos = photosdb.photos(to_date=dt.datetime(2018, 10, 28))
|
||||
assert len(photos) == 6
|
||||
|
||||
photos = photosdb.photos(
|
||||
from_date=dt.datetime(2018, 9, 28), to_date=dt.datetime(2018, 9, 29)
|
||||
)
|
||||
assert len(photos) == 4
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -57,6 +57,7 @@ ALBUMS = [
|
||||
"EmptyAlbum",
|
||||
"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
@ -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"])
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -1,165 +0,0 @@
|
||||
import pytest
|
||||
|
||||
|
||||
from osxphotos._constants import _UNKNOWN_PERSON
|
||||
|
||||
PHOTOS_DB = "./tests/Test-10.14.5.photoslibrary/database/photos.db"
|
||||
KEYWORDS = [
|
||||
"Kids",
|
||||
"wedding",
|
||||
"flowers",
|
||||
"England",
|
||||
"London",
|
||||
"London 2018",
|
||||
"St. James's Park",
|
||||
"UK",
|
||||
"United Kingdom",
|
||||
]
|
||||
PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON]
|
||||
ALBUMS = ["Pumpkin Farm"]
|
||||
KEYWORDS_DICT = {
|
||||
"Kids": 4,
|
||||
"wedding": 2,
|
||||
"flowers": 1,
|
||||
"England": 1,
|
||||
"London": 1,
|
||||
"London 2018": 1,
|
||||
"St. James's Park": 1,
|
||||
"UK": 1,
|
||||
"United Kingdom": 1,
|
||||
}
|
||||
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1, _UNKNOWN_PERSON: 1}
|
||||
ALBUM_DICT = {"Pumpkin Farm": 3}
|
||||
|
||||
|
||||
def test_init():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert isinstance(photosdb, osxphotos.PhotosDB)
|
||||
|
||||
|
||||
def test_db_version():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
|
||||
assert photosdb.db_version == "4016"
|
||||
|
||||
|
||||
def test_persons():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "Katie" in photosdb.persons
|
||||
assert collections.Counter(PERSONS) == collections.Counter(photosdb.persons)
|
||||
|
||||
|
||||
def test_keywords():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "wedding" in photosdb.keywords
|
||||
assert collections.Counter(KEYWORDS) == collections.Counter(photosdb.keywords)
|
||||
|
||||
|
||||
def test_album_names():
|
||||
import osxphotos
|
||||
import collections
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
assert "Pumpkin Farm" in photosdb.albums
|
||||
assert collections.Counter(ALBUMS) == collections.Counter(photosdb.albums)
|
||||
|
||||
|
||||
def test_keywords_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
keywords = photosdb.keywords_as_dict
|
||||
assert keywords["wedding"] == 2
|
||||
assert keywords == KEYWORDS_DICT
|
||||
|
||||
|
||||
def test_persons_as_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
persons = photosdb.persons_as_dict
|
||||
assert persons["Maria"] == 1
|
||||
assert persons == PERSONS_DICT
|
||||
|
||||
|
||||
def test_albums_as_dict():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
albums = photosdb.albums_as_dict
|
||||
assert albums["Pumpkin Farm"] == 3
|
||||
assert albums == ALBUM_DICT
|
||||
|
||||
|
||||
def test_attributes():
|
||||
import datetime
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["15uNd7%8RguTEgNPKHfTWw"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.keywords == ["Kids"]
|
||||
assert p.original_filename == "Pumkins2.jpg"
|
||||
assert p.filename == "Pumkins2.jpg"
|
||||
assert p.date == datetime.datetime(
|
||||
2018, 9, 28, 16, 7, 7, 0, datetime.timezone(datetime.timedelta(seconds=-14400))
|
||||
)
|
||||
assert p.description == "Girl holding pumpkin"
|
||||
assert p.title == "I found one!"
|
||||
assert p.albums == ["Pumpkin Farm"]
|
||||
assert p.persons == ["Katie"]
|
||||
assert p.path.endswith(
|
||||
"/tests/Test-10.14.5.photoslibrary/Masters/2019/07/27/20190727-131650/Pumkins2.jpg"
|
||||
)
|
||||
assert p.ismissing == False
|
||||
|
||||
|
||||
def test_missing():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["od0fmC7NQx+ayVr+%i06XA"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
assert p.path == None
|
||||
assert p.ismissing == True
|
||||
|
||||
|
||||
def test_count():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos()
|
||||
assert len(photos) == 7
|
||||
|
||||
|
||||
def test_keyword_2():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(keywords=["wedding"])
|
||||
assert len(photos) == 2
|
||||
|
||||
|
||||
def test_keyword_not_in_album():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
|
||||
# find all photos with keyword "Kids" not in the album "Pumpkin Farm"
|
||||
photos1 = photosdb.photos(albums=["Pumpkin Farm"])
|
||||
photos2 = photosdb.photos(keywords=["Kids"])
|
||||
photos3 = [p for p in photos2 if p not in photos1]
|
||||
assert len(photos3) == 1
|
||||
assert photos3[0].uuid == "od0fmC7NQx+ayVr+%i06XA"
|
||||
@ -356,7 +356,7 @@ def photosdb_cloud():
|
||||
|
||||
|
||||
def test_lookup(photosdb_places):
|
||||
""" 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(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user