Implemented PhotoInfo.owner, AlbumInfo.owner, #216, #239

This commit is contained in:
Rhet Turnbull 2021-09-25 22:33:37 -07:00
parent e5b2d2ee45
commit c4b7c2623f
17 changed files with 232 additions and 51 deletions

View File

@ -3,3 +3,4 @@ include README.rst
include osxphotos/templates/*
include osxphotos/phototemplate.tx
include osxphotos/phototemplate.md
include osxphotos/queries/*

View File

@ -1702,7 +1702,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.84'
{osxphotos_version} The osxphotos version, e.g. '0.42.85'
{osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified for
@ -2516,7 +2516,12 @@ Returns a [PlaceInfo](#PlaceInfo) object with reverse geolocation data or None i
#### `shared`
Returns True if photo is in a shared album, otherwise False.
**Note**: *Only valid on Photos 5 / MacOS 10.15+; on Photos <= 4, returns None instead of True/False.
**Note**: *Only valid on Photos 5 / MacOS 10.15+; on Photos <= 4, returns None.
#### `owner`
Returns full name of the photo owner (person who shared the photo) for shared photos or None if photo is not shared. Also returns None if you are the person who shared the photo.
**Note**: *Only valid on Photos 5 / MacOS 10.15+; on Photos <= 4, returns None.
#### `comments`
Returns list of [CommentInfo](#commentinfo) objects for comments on shared photos or empty list if no comments.
@ -2890,6 +2895,11 @@ Photos Library
#### `parent`
Returns a [FolderInfo](#FolderInfo) object representing the albums parent folder or `None` if album is not a in a folder.
#### `owner`
Returns full name of the album owner (person who shared the album) for shared albums or None if album is not shared.
**Note**: *Only valid on Photos 5 / MacOS 10.15+; on Photos <= 4, returns None.
### ImportInfo
PhotosDB.import_info returns a list of ImportInfo objects. Each ImportInfo object represents an import session in the library. PhotoInfo.import_info returns a single ImportInfo object representing the import session for the photo (or `None` if no associated import session).
@ -3561,7 +3571,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.84'|
|{osxphotos_version}|The osxphotos version, e.g. '0.42.85'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{album}|Album(s) photo is contained in|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|

View File

@ -1,3 +1,4 @@
""" version info """
__version__ = "0.42.84"
__version__ = "0.42.85"

View File

@ -22,6 +22,7 @@ from ._constants import (
AlbumSortOrder,
)
from .datetime_utils import get_local_tz
from .query_builder import get_query
def sort_list_by_keys(values, sort_keys):
@ -131,6 +132,22 @@ class AlbumInfoBaseClass:
def photos(self):
return []
@property
def owner(self):
"""Return name of photo owner for shared album (Photos 5+ only), or None if not shared"""
if self._db._db_version <= _PHOTOS_4_VERSION:
return None
try:
return self._owner
except AttributeError:
query = get_query(
"cloud_album_owner", photos_ver=self._db._photos_ver, uuid=self.uuid
)
result = self._db.execute(query).fetchone()
self._owner = result[0] if result else None
return self._owner
def __len__(self):
"""return number of photos contained in album"""
return len(self.photos)

View File

@ -4103,7 +4103,11 @@ def repl(ctx, cli_obj, db):
get_photo = photosdb.get_photo
show = _show_photo
get_selected = _get_selected(photosdb)
selected = get_selected()
try:
selected = get_selected()
except Exception:
# get_selected sometimes fails
selected = []
def inspect(obj):
"""inspect object"""

View File

@ -38,6 +38,7 @@ from ..albuminfo import AlbumInfo, ImportInfo
from ..personinfo import FaceInfo, PersonInfo
from ..phototemplate import PhotoTemplate, RenderOptions
from ..placeinfo import PlaceInfo4, PlaceInfo5
from ..query_builder import get_query
from ..text_detection import detect_text
from ..uti import get_preferred_uti_extension, get_uti_for_extension
from ..utils import _debug, _get_resource_loc, findfiles
@ -1098,6 +1099,22 @@ class PhotoInfo:
logging.warning(f"Did not find signature for {self.uuid} in _db_signatures")
return duplicates
@property
def owner(self):
"""Return name of photo owner for shared photos (Photos 5+ only), or None if not shared"""
if self._db._db_version <= _PHOTOS_4_VERSION:
return None
try:
return self._owner
except AttributeError:
query = get_query(
"shared_owner", photos_ver=self._db._photos_ver, uuid=self.uuid
)
result = self._db.execute(query).fetchone()
self._owner = result[0] if result else None
return self._owner
def render_template(
self, template_str: str, options: Optional[RenderOptions] = None
):

View File

@ -330,6 +330,8 @@ class PhotosDB:
else:
self._process_database5()
self._db_connection, _ = self.get_db_connection()
@property
def keywords_as_dict(self):
"""return keywords as dict of keyword, count in reverse sorted order (descending)"""
@ -790,8 +792,8 @@ class PhotosDB:
"creation_date": album[8],
"start_date": None, # Photos 5 only
"end_date": None, # Photos 5 only
"customsortascending": None, # Photos 5 only
"customsortkey": None, # Photos 5 only
"customsortascending": None, # Photos 5 only
"customsortkey": None, # Photos 5 only
}
# get details about folders
@ -1104,7 +1106,9 @@ class PhotosDB:
# get info on special types
self._dbphotos[uuid]["specialType"] = row[25]
self._dbphotos[uuid]["masterModelID"] = row[26]
self._dbphotos[uuid]["pk"] = row[26] # same as masterModelID, to match Photos 5
self._dbphotos[uuid]["pk"] = row[
26
] # same as masterModelID, to match Photos 5
self._dbphotos[uuid]["panorama"] = True if row[25] == 1 else False
self._dbphotos[uuid]["slow_mo"] = True if row[25] == 2 else False
self._dbphotos[uuid]["time_lapse"] = True if row[25] == 3 else False
@ -3354,6 +3358,10 @@ class PhotosDB:
return photos
def execute(self, sql):
"""Execute sql statement and return cursor"""
return self._db_connection.cursor().execute(sql)
def _duplicate_signature(self, uuid):
"""Compute a signature for finding possible duplicates"""
return (
@ -3381,6 +3389,10 @@ class PhotosDB:
"""
return len(self._dbphotos)
def __del__(self):
if getattr(self, "_db_connection", None):
self._db_connection.close()
def _get_photos_by_attribute(photos, attribute, values, ignore_case):
"""Search for photos based on values being in PhotoInfo.attribute

View File

@ -0,0 +1,5 @@
# Query templates
This folder contains sql query templates for getting various photo properties
The query templates must be rendered with mako (see query_builder.py)

View File

@ -0,0 +1,4 @@
-- Get owner name for shared iCloud album
SELECT ZGENERICALBUM.ZCLOUDOWNERFULLNAME AS OWNER_FULLNAME
FROM ZGENERICALBUM
WHERE ZGENERICALBUM.ZUUID = '${uuid}'

View File

@ -0,0 +1,25 @@
-- Get the owner name of person who owns a photo in a shared album
WITH case1 AS
(
-- Case where someone has invited you to a shared album
-- Need to get the owner of the shared album
SELECT ZGENERICALBUM.ZCLOUDOWNERFULLNAME as OWNER_FULLNAME
FROM ZGENERICALBUM
JOIN ${asset_table} ON ${asset_table}.ZCLOUDOWNERHASHEDPERSONID = ZGENERICALBUM.ZCLOUDOWNERHASHEDPERSONID
WHERE ${asset_table}.ZUUID = "${uuid}"
),
case2 AS
(
-- Case where you have invited someone to a shared album
-- Need to get the data for person who was invited to the album
SELECT
ZCLOUDSHAREDALBUMINVITATIONRECORD.ZINVITEEFULLNAME AS OWNER_FULLNAME
FROM ZCLOUDSHAREDALBUMINVITATIONRECORD
JOIN ${asset_table} ON ${asset_table}.ZCLOUDOWNERHASHEDPERSONID = ZCLOUDSHAREDALBUMINVITATIONRECORD.ZINVITEEHASHEDPERSONID
WHERE ${asset_table}.ZUUID = "${uuid}"
ORDER BY ZCLOUDSHAREDALBUMINVITATIONRECORD.Z_PK
LIMIT 1
)
SELECT * FROM case1
UNION
SELECT * FROM case2 WHERE NOT EXISTS (SELECT * FROM case1)

View File

@ -0,0 +1,6 @@
-- Get title of a photo with given UUID
SELECT
ZADDITIONALASSETATTRIBUTES.ZTITLE
FROM ZADDITIONALASSETATTRIBUTES
JOIN ${asset_table} ON ${asset_table}.Z_PK = ZADDITIONALASSETATTRIBUTES.ZASSET
WHERE ${asset_table}.ZUUID = "${uuid}"

View File

@ -0,0 +1,36 @@
"""Build sql queries from template to retrieve info from the database"""
import os.path
import pathlib
from functools import lru_cache
from mako.template import Template
from ._constants import _DB_TABLE_NAMES
QUERY_DIR = os.path.join(os.path.dirname(__file__), "queries")
def get_query(query_name, photos_ver, **kwargs):
"""Return sqlite query string for an attribute and a given database version"""
# there can be a single query for multiple database versions or separate queries for each version
# try generic version first (most common case), if that fails, look for version specific query
query_string = _get_query_string(query_name, photos_ver)
asset_table = _DB_TABLE_NAMES[photos_ver]["ASSET"]
query_template = Template(query_string)
return query_template.render(asset_table=asset_table, **kwargs)
@lru_cache(maxsize=None)
def _get_query_string(query_name, photos_ver):
"""Return sqlite query string for an attribute and a given database version"""
query_file = pathlib.Path(QUERY_DIR) / f"{query_name}.sql.mako"
if not query_file.is_file():
query_file = pathlib.Path(QUERY_DIR) / f"{query_name}_{photos_ver}.sql.mako"
if not query_file.is_file():
raise FileNotFoundError(f"Query file '{query_file}' not found")
with open(query_file, "r") as f:
query_string = f.read()
return query_string

View File

@ -337,7 +337,7 @@ def test_attributes(photosdb):
def test_attributes_2(photosdb):
""" Test attributes including height, width, etc """
"""Test attributes including height, width, etc"""
import datetime
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
@ -517,39 +517,39 @@ 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:
assert p.intrash
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:
assert not p.intrash
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
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
assert "Maria" in p.persons
@ -557,7 +557,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
assert "Maria" in p.persons
@ -565,7 +565,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
assert "Maria" in p.persons
@ -573,7 +573,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
@ -594,7 +594,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)
@ -617,7 +617,7 @@ def test_get_library_path(photosdb):
def test_get_db_connection(photosdb):
""" Test PhotosDB.get_db_connection """
"""Test PhotosDB.get_db_connection"""
import sqlite3
conn, cursor = photosdb.get_db_connection()
@ -926,7 +926,7 @@ def test_export_14(caplog, photosdb):
def test_eq(photosdb):
""" Test equality of two PhotoInfo objects """
"""Test equality of two PhotoInfo objects"""
import osxphotos
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
@ -936,7 +936,7 @@ def test_eq(photosdb):
def test_eq_2(photosdb):
""" Test equality of two PhotoInfo objects when one has memoized property """
"""Test equality of two PhotoInfo objects when one has memoized property"""
import osxphotos
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
@ -960,7 +960,7 @@ def test_photosdb_repr():
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb2 = eval(repr(photosdb))
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name"]
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name", "_db_connection"]
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
}
@ -999,7 +999,7 @@ def test_from_to_date(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
@ -1016,7 +1016,7 @@ def test_date_invalid():
def test_date_modified_invalid(photosdb):
""" Test date modified is invalid """
"""Test date modified is invalid"""
from datetime import datetime, timedelta, timezone
# UUID_DICT["date_invalid"] has an invalid modified date that's
@ -1028,7 +1028,7 @@ def test_date_modified_invalid(photosdb):
def test_uti(photosdb):
""" test uti """
"""test uti"""
for uuid, uti in UTI_DICT.items():
photo = photosdb.get_photo(uuid)
@ -1037,7 +1037,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)
@ -1050,7 +1050,7 @@ def test_raw(photosdb):
def test_is_reference(photosdb):
""" test isreference """
"""test isreference"""
photo = photosdb.get_photo(UUID_IS_REFERENCE)
assert photo.isreference
@ -1059,7 +1059,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"])
@ -1121,7 +1121,7 @@ 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

View File

@ -1066,7 +1066,7 @@ def test_photosdb_repr():
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb2 = eval(repr(photosdb))
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name"]
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name", "_db_connection"]
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
}

View File

@ -0,0 +1,43 @@
# Test cloud photos and album owner
import pytest
import osxphotos
PHOTOS_DB_CLOUD = "./tests/Test-Cloud-10.15.6.photoslibrary/"
PHOTOS_DB_NOT_CLOUD = "./tests/Test-10.15.6.photoslibrary/"
UUID_DICT = {
"not_cloudasset": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
"owner": "7572C53E-1D6A-410C-A2B1-18CCA3B5AD9F",
}
@pytest.fixture(scope="module")
def photosdb_cloud():
return osxphotos.PhotosDB(dbfile=PHOTOS_DB_CLOUD)
@pytest.fixture(scope="module")
def photosdb_nocloud():
return osxphotos.PhotosDB(dbfile=PHOTOS_DB_NOT_CLOUD)
def test_album_owner_cloud(photosdb_cloud):
album = [a for a in photosdb_cloud.album_info_shared if a.title == "osxphotos"][0]
assert album.owner == "Rhet Turnbull"
def test_album_owner_not_cloud(photosdb_nocloud):
album = [a for a in photosdb_nocloud.album_info if a.title == "Test Album"][0]
assert album.owner is None
def test_photo_owner_cloud(photosdb_cloud):
photo = photosdb_cloud.get_photo(UUID_DICT["owner"])
assert photo.owner == "Rhet Turnbull"
def test_photo_owner_nocloud(photosdb_nocloud):
photo = photosdb_nocloud.get_photo(UUID_DICT["not_cloudasset"])
assert photo.owner is None

View File

@ -266,7 +266,7 @@ def test_attributes(photosdb):
def test_attributes_2(photosdb):
""" Test attributes including height, width, etc """
"""Test attributes including height, width, etc"""
import datetime
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
@ -338,7 +338,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]
@ -346,7 +346,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]
@ -448,13 +448,13 @@ 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:
assert p.intrash
@ -462,7 +462,7 @@ def test_photos_intrash_2(photosdb):
def test_photos_not_intrash(photosdb):
""" test PhotosDB.photos(intrash=False) """
"""test PhotosDB.photos(intrash=False)"""
photos = photosdb.photos(intrash=False)
for p in photos:
assert not p.intrash
@ -470,19 +470,19 @@ def test_photos_not_intrash(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
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_not_intrash(photosdb):
""" Test PhotoInfo.intrash """
"""Test PhotoInfo.intrash"""
p = photosdb.photos(uuid=[UUID_DICT["not_intrash"]])[0]
assert not p.intrash
@ -517,7 +517,7 @@ def test_photosdb_repr():
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb2 = eval(repr(photosdb))
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name"]
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name", "_db_connection"]
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
}
@ -562,7 +562,7 @@ def test_multi_person(photosdb):
def test_date_invalid(photosdb):
""" Test date is invalid """
"""Test date is invalid"""
from datetime import datetime, timedelta, timezone
photos = photosdb.photos(uuid=[UUID_DICT["date_invalid"]])
@ -574,7 +574,7 @@ def test_date_invalid(photosdb):
def test_date_modified_invalid(photosdb):
""" Test date modified is invalid """
"""Test date modified is invalid"""
photos = photosdb.photos(uuid=[UUID_DICT["date_invalid"]])
assert len(photos) == 1
@ -583,7 +583,7 @@ def test_date_modified_invalid(photosdb):
def test_date_modified(photosdb):
""" Test date modified for photo that has been edited """
"""Test date modified for photo that has been edited"""
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
p = photos[0]
@ -600,7 +600,7 @@ def test_date_modified(photosdb):
def test_date_modified_none(photosdb):
""" Test date modified for a photo that hasn't been edited """
"""Test date modified for a photo that hasn't been edited"""
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
p = photos[0]
@ -638,7 +638,7 @@ def test_raw(photosdb):
def test_raw():
""" Test various raw properties """
"""Test various raw properties"""
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
@ -654,7 +654,7 @@ def test_raw():
def test_is_reference(photosdb):
""" test isreference """
"""test isreference"""
photo = photosdb.get_photo(UUID_IS_REFERENCE)
assert photo.isreference
@ -663,7 +663,7 @@ def test_is_reference(photosdb):
def test_adjustments(photosdb):
""" test adjustments/AdjustmentsInfo (not implemented for 10.14) """
"""test adjustments/AdjustmentsInfo (not implemented for 10.14)"""
from osxphotos.adjustmentsinfo import AdjustmentsInfo
photo = photosdb.get_photo(UUID_DICT["has_adjustments"])
@ -671,7 +671,7 @@ 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

View File

@ -1033,7 +1033,7 @@ def test_photosdb_repr():
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb2 = eval(repr(photosdb))
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name"]
ignore_keys = ["_tmp_db", "_tempdir", "_tempdir_name", "_db_connection"]
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
}