parent
e5b2d2ee45
commit
c4b7c2623f
@ -3,3 +3,4 @@ include README.rst
|
||||
include osxphotos/templates/*
|
||||
include osxphotos/phototemplate.tx
|
||||
include osxphotos/phototemplate.md
|
||||
include osxphotos/queries/*
|
||||
16
README.md
16
README.md
@ -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|
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.42.84"
|
||||
__version__ = "0.42.85"
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"""
|
||||
|
||||
@ -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
|
||||
):
|
||||
|
||||
@ -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
|
||||
|
||||
5
osxphotos/queries/README.md
Normal file
5
osxphotos/queries/README.md
Normal 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)
|
||||
4
osxphotos/queries/cloud_album_owner.sql.mako
Normal file
4
osxphotos/queries/cloud_album_owner.sql.mako
Normal file
@ -0,0 +1,4 @@
|
||||
-- Get owner name for shared iCloud album
|
||||
SELECT ZGENERICALBUM.ZCLOUDOWNERFULLNAME AS OWNER_FULLNAME
|
||||
FROM ZGENERICALBUM
|
||||
WHERE ZGENERICALBUM.ZUUID = '${uuid}'
|
||||
25
osxphotos/queries/shared_owner.sql.mako
Normal file
25
osxphotos/queries/shared_owner.sql.mako
Normal 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)
|
||||
6
osxphotos/queries/title.sql.mako
Normal file
6
osxphotos/queries/title.sql.mako
Normal 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}"
|
||||
36
osxphotos/query_builder.py
Normal file
36
osxphotos/query_builder.py
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
43
tests/test_cloud_owner_catalina.py
Normal file
43
tests/test_cloud_owner_catalina.py
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user