Added shallow json() option, #1038 (#1040)

This commit is contained in:
Rhet Turnbull
2023-04-08 11:10:29 -07:00
committed by GitHub
parent f4a743468d
commit d2b7783125
4 changed files with 91 additions and 30 deletions

View File

@@ -1212,7 +1212,7 @@ class PhotoExporter:
# set data in the database # set data in the database
with export_db.create_or_get_file_record(dest_str, self.photo.uuid) as rec: with export_db.create_or_get_file_record(dest_str, self.photo.uuid) as rec:
rec.photoinfo = self.photo.json() rec.photoinfo = self.photo.json(shallow=True)
rec.export_options = options.bit_flags rec.export_options = options.bit_flags
# don't set src_sig as that is set above before any modifications by convert_to_jpeg or exiftool # don't set src_sig as that is set above before any modifications by convert_to_jpeg or exiftool
if not options.ignore_signature: if not options.ignore_signature:

View File

@@ -1888,14 +1888,46 @@ class PhotoInfo:
"width": self.width, "width": self.width,
} }
def json(self): def json(self, indent: int | None = None, shallow: bool = False) -> str:
"""Return JSON representation""" """Return JSON representation
Args:
indent: indent level for JSON, if None, no indent
shallow: if True, return shallow JSON representation (does not contain folder_info, person_info, etc.)
Returns:
JSON string
"""
def default(o): def default(o):
if isinstance(o, (datetime.date, datetime.datetime)): if isinstance(o, (datetime.date, datetime.datetime)):
return o.isoformat() return o.isoformat()
dict_data = self.asdict() dict_data = self.asdict()
if shallow:
# delete items that are not needed for shallow JSON
# these are removed to match behavior of osxphotos < 0.59.0 (See #999, #1039)
for key in [
"adjustments",
"album_info",
"burst_album_info",
"burst_albums",
"burst_default_pick",
"burst_key",
"burst_photos",
"burst_selected",
"cloud_metadata",
"import_info",
"labels_normalized",
"path_derivatives",
"person_info",
"project_info",
"search_info_normalized",
"search_info",
]:
del dict_data[key]
for k, v in dict_data.items(): for k, v in dict_data.items():
# sort lists such as keywords so JSON is consistent # sort lists such as keywords so JSON is consistent
# but do not sort certain items like location # but do not sort certain items like location
@@ -1903,7 +1935,7 @@ class PhotoInfo:
continue continue
if v and isinstance(v, (list, tuple)) and not isinstance(v[0], dict): if v and isinstance(v, (list, tuple)) and not isinstance(v[0], dict):
dict_data[k] = sorted(v, key=lambda v: v if v is not None else "") dict_data[k] = sorted(v, key=lambda v: v if v is not None else "")
return json.dumps(dict_data, sort_keys=True, default=default) return json.dumps(dict_data, sort_keys=True, default=default, indent=indent)
def _json_hexdigest(self): def _json_hexdigest(self):
"""JSON for use by hexdigest()""" """JSON for use by hexdigest()"""

View File

@@ -1,6 +1,7 @@
""" Basic tests for Photos 5 on MacOS 10.15.7 """ """ Basic tests for Photos 5 on MacOS 10.15.7 """
import datetime import datetime
import json
import os import os
import os.path import os.path
import pathlib import pathlib
@@ -341,19 +342,16 @@ def test_init5(mocker):
def test_db_len(photosdb): def test_db_len(photosdb):
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS # assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
assert len(photosdb) == PHOTOS_DB_LEN assert len(photosdb) == PHOTOS_DB_LEN
def test_db_version(photosdb): def test_db_version(photosdb):
# assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS # assert photosdb.db_version in osxphotos._TESTED_DB_VERSIONS
assert photosdb.db_version == "6000" assert photosdb.db_version == "6000"
def test_persons(photosdb): def test_persons(photosdb):
assert "Katie" in photosdb.persons assert "Katie" in photosdb.persons
assert Counter(PERSONS) == Counter(photosdb.persons) assert Counter(PERSONS) == Counter(photosdb.persons)
@@ -363,40 +361,34 @@ def test_photos_version(photosdb):
def test_keywords(photosdb): def test_keywords(photosdb):
assert "wedding" in photosdb.keywords assert "wedding" in photosdb.keywords
assert Counter(KEYWORDS) == Counter(photosdb.keywords) assert Counter(KEYWORDS) == Counter(photosdb.keywords)
def test_album_names(photosdb): def test_album_names(photosdb):
assert "Pumpkin Farm" in photosdb.albums assert "Pumpkin Farm" in photosdb.albums
assert Counter(ALBUMS) == Counter(photosdb.albums) assert Counter(ALBUMS) == Counter(photosdb.albums)
def test_keywords_dict(photosdb): def test_keywords_dict(photosdb):
keywords = photosdb.keywords_as_dict keywords = photosdb.keywords_as_dict
assert keywords["wedding"] == 3 assert keywords["wedding"] == 3
assert keywords == KEYWORDS_DICT assert keywords == KEYWORDS_DICT
def test_persons_as_dict(photosdb): def test_persons_as_dict(photosdb):
persons = photosdb.persons_as_dict persons = photosdb.persons_as_dict
assert persons["Maria"] == 2 assert persons["Maria"] == 2
assert persons == PERSONS_DICT assert persons == PERSONS_DICT
def test_albums_as_dict(photosdb): def test_albums_as_dict(photosdb):
albums = photosdb.albums_as_dict albums = photosdb.albums_as_dict
assert albums["Pumpkin Farm"] == 3 assert albums["Pumpkin Farm"] == 3
assert albums == ALBUM_DICT assert albums == ALBUM_DICT
def test_album_sort_order(photosdb): def test_album_sort_order(photosdb):
album = [a for a in photosdb.album_info if a.title == "Pumpkin Farm"][0] album = [a for a in photosdb.album_info if a.title == "Pumpkin Farm"][0]
photos = album.photos photos = album.photos
@@ -405,14 +397,12 @@ def test_album_sort_order(photosdb):
def test_album_empty_album(photosdb): def test_album_empty_album(photosdb):
album = [a for a in photosdb.album_info if a.title == "EmptyAlbum"][0] album = [a for a in photosdb.album_info if a.title == "EmptyAlbum"][0]
photos = album.photos photos = album.photos
assert photos == [] assert photos == []
def test_attributes(photosdb): def test_attributes(photosdb):
photos = photosdb.photos(uuid=["D79B8D77-BFFC-460B-9312-034F2877D35B"]) photos = photosdb.photos(uuid=["D79B8D77-BFFC-460B-9312-034F2877D35B"])
assert len(photos) == 1 assert len(photos) == 1
p = photos[0] p = photos[0]
@@ -484,7 +474,6 @@ def test_attributes_2(photosdb):
def test_missing(photosdb): def test_missing(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["missing"]]) photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
assert len(photos) == 1 assert len(photos) == 1
p = photos[0] p = photos[0]
@@ -493,7 +482,6 @@ def test_missing(photosdb):
def test_favorite(photosdb): def test_favorite(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]]) photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
assert len(photos) == 1 assert len(photos) == 1
p = photos[0] p = photos[0]
@@ -501,7 +489,6 @@ def test_favorite(photosdb):
def test_not_favorite(photosdb): def test_not_favorite(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_favorite"]]) photos = photosdb.photos(uuid=[UUID_DICT["not_favorite"]])
assert len(photos) == 1 assert len(photos) == 1
p = photos[0] p = photos[0]
@@ -509,7 +496,6 @@ def test_not_favorite(photosdb):
def test_hidden(photosdb): def test_hidden(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["hidden"]]) photos = photosdb.photos(uuid=[UUID_DICT["hidden"]])
assert len(photos) == 1 assert len(photos) == 1
p = photos[0] p = photos[0]
@@ -517,7 +503,6 @@ def test_hidden(photosdb):
def test_not_hidden(photosdb): def test_not_hidden(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]]) photos = photosdb.photos(uuid=[UUID_DICT["not_hidden"]])
assert len(photos) == 1 assert len(photos) == 1
p = photos[0] p = photos[0]
@@ -656,7 +641,6 @@ def test_not_ismovie(photosdb):
def test_count(photosdb): def test_count(photosdb):
photos = photosdb.photos() photos = photosdb.photos()
assert len(photos) == PHOTOS_NOT_IN_TRASH_LEN assert len(photos) == PHOTOS_NOT_IN_TRASH_LEN
@@ -735,13 +719,11 @@ def test_photoinfo_not_intrash(photosdb):
def test_keyword_2(photosdb): def test_keyword_2(photosdb):
photos = photosdb.photos(keywords=["wedding"]) photos = photosdb.photos(keywords=["wedding"])
assert len(photos) == 2 # won't show the one in the trash assert len(photos) == 2 # won't show the one in the trash
def test_keyword_not_in_album(photosdb): def test_keyword_not_in_album(photosdb):
# find all photos with keyword "Kids" not in the album "Pumpkin Farm" # find all photos with keyword "Kids" not in the album "Pumpkin Farm"
photos1 = photosdb.photos(albums=["Pumpkin Farm"]) photos1 = photosdb.photos(albums=["Pumpkin Farm"])
photos2 = photosdb.photos(keywords=["Kids"]) photos2 = photosdb.photos(keywords=["Kids"])
@@ -758,20 +740,17 @@ def test_album_folder_name(photosdb):
def test_multi_person(photosdb): def test_multi_person(photosdb):
photos = photosdb.photos(persons=["Katie", "Suzy"]) photos = photosdb.photos(persons=["Katie", "Suzy"])
assert len(photos) == 3 assert len(photos) == 3
def test_get_db_path(photosdb): def test_get_db_path(photosdb):
db_path = photosdb.db_path db_path = photosdb.db_path
assert db_path.endswith(PHOTOS_DB_PATH) assert db_path.endswith(PHOTOS_DB_PATH)
def test_get_library_path(photosdb): def test_get_library_path(photosdb):
lib_path = photosdb.library_path lib_path = photosdb.library_path
assert lib_path.endswith(PHOTOS_LIBRARY_PATH) assert lib_path.endswith(PHOTOS_LIBRARY_PATH)
@@ -1080,14 +1059,12 @@ def test_eq_2():
def test_not_eq(photosdb): def test_not_eq(photosdb):
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]]) photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
photos2 = photosdb.photos(uuid=[UUID_DICT["missing"]]) photos2 = photosdb.photos(uuid=[UUID_DICT["missing"]])
assert photos1[0] != photos2[0] assert photos1[0] != photos2[0]
def test_photosdb_repr(): def test_photosdb_repr():
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB) photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb2 = eval(repr(photosdb)) photosdb2 = eval(repr(photosdb))
@@ -1098,7 +1075,6 @@ def test_photosdb_repr():
def test_photosinfo_repr(photosdb): def test_photosinfo_repr(photosdb):
photos = photosdb.photos(uuid=[UUID_DICT["favorite"]]) photos = photosdb.photos(uuid=[UUID_DICT["favorite"]])
photo = photos[0] photo = photos[0]
photo2 = eval(repr(photo)) photo2 = eval(repr(photo))
@@ -1502,3 +1478,33 @@ def test_fingerprint(photosdb):
for uuid, fingerprint in UUID_FINGERPRINT.items(): for uuid, fingerprint in UUID_FINGERPRINT.items():
photo = photosdb.get_photo(uuid) photo = photosdb.get_photo(uuid)
assert photo.fingerprint == fingerprint assert photo.fingerprint == fingerprint
def test_asdict(photosdb: osxphotos.PhotosDB):
"""Test PhotoInfo.asdict()"""
photo = photosdb.get_photo(UUID_DICT["favorite"])
photo_dict = photo.asdict()
assert photo_dict["favorite"]
def test_json(photosdb: osxphotos.PhotosDB):
"""Test PhotoInfo.json()"""
photo = photosdb.get_photo(UUID_DICT["favorite"])
photo_dict = json.loads(photo.json())
assert photo_dict["favorite"]
def test_json_indent(photosdb: osxphotos.PhotosDB):
"""Test PhotoInfo.json() with indent"""
photo = photosdb.get_photo(UUID_DICT["favorite"])
photo_dict = json.loads(photo.json(indent=4))
assert photo_dict["favorite"]
assert "album_info" in photo_dict
def test_json_shallow(photosdb: osxphotos.PhotosDB):
"""Test PhotoInfo.json() with shallow=True"""
photo = photosdb.get_photo(UUID_DICT["favorite"])
photo_dict = json.loads(photo.json(shallow=True))
assert photo_dict["favorite"]
assert "album_info" not in photo_dict

View File

@@ -1,5 +1,6 @@
"""Test macOS 13.0 Photos library""" """Test macOS 13.0 Photos library"""
import json
from collections import namedtuple from collections import namedtuple
import pytest import pytest
@@ -660,7 +661,6 @@ def test_keyword_2(photosdb):
def test_keyword_not_in_album(photosdb): def test_keyword_not_in_album(photosdb):
# find all photos with keyword "Kids" not in the album "Pumpkin Farm" # find all photos with keyword "Kids" not in the album "Pumpkin Farm"
photos1 = photosdb.photos(albums=["Pumpkin Farm"]) photos1 = photosdb.photos(albums=["Pumpkin Farm"])
photos2 = photosdb.photos(keywords=["Kids"]) photos2 = photosdb.photos(keywords=["Kids"])
@@ -1273,3 +1273,26 @@ def test_person_feature_less(photosdb):
photo = photosdb.get_photo(UUID_PERSON_NOT_FEATURE_LESS) photo = photosdb.get_photo(UUID_PERSON_NOT_FEATURE_LESS)
assert not photo.person_info[0].feature_less assert not photo.person_info[0].feature_less
def test_json(photosdb: osxphotos.PhotosDB):
"""Test PhotoInfo.json()"""
photo = photosdb.get_photo(UUID_DICT["favorite"])
photo_dict = json.loads(photo.json())
assert photo_dict["favorite"]
def test_json_indent(photosdb: osxphotos.PhotosDB):
"""Test PhotoInfo.json() with indent"""
photo = photosdb.get_photo(UUID_DICT["favorite"])
photo_dict = json.loads(photo.json(indent=4))
assert photo_dict["favorite"]
assert "album_info" in photo_dict
def test_json_shallow(photosdb: osxphotos.PhotosDB):
"""Test PhotoInfo.json() with shallow=True"""
photo = photosdb.get_photo(UUID_DICT["favorite"])
photo_dict = json.loads(photo.json(shallow=True))
assert photo_dict["favorite"]
assert "album_info" not in photo_dict