Feature person favorite 940 (#950)

* Added person favorite for #940

* Added person.feature_less #940 (but not 100% sure it's right yet)

* Added tests for PersonInfo.favorite, PersonInfo.feature_less
This commit is contained in:
Rhet Turnbull
2023-01-22 11:53:08 -08:00
committed by GitHub
parent 381141bc09
commit 29968269ff
20 changed files with 184 additions and 94 deletions

View File

@@ -3,8 +3,10 @@
import json
import logging
import math
from collections import namedtuple
from functools import cached_property
import osxphotos
__all__ = ["PersonInfo", "FaceInfo", "rotate_image_point"]
@@ -15,7 +17,7 @@ MPRI_Reg_Rect = namedtuple("MPRI_Reg_Rect", ["x", "y", "h", "w"])
class PersonInfo:
"""Info about a person in the Photos library"""
def __init__(self, db=None, pk=None):
def __init__(self, db: "osxphotos.PhotosDB", pk: int):
"""Creates a new PersonInfo instance
Arguments:
@@ -25,8 +27,8 @@ class PersonInfo:
Returns:
PersonInfo instance
"""
self._db = db
self._pk = pk
self._db: "osxphotos.PhotosDB" = db
self._pk: int = pk
person = self._db._dbpersons_pk[pk]
self.uuid = person["uuid"]
@@ -72,6 +74,33 @@ class PersonInfo:
# no faces
return []
@property
def favorite(self):
"""Returns True if person is a favorite, False otherwise; Photos 5+ only; returns False on Photos <= 4"""
return self._db._dbpersons_pk[self._pk]["type"] == 1
@property
def sort_order(self):
"""Returns sort order of person; favorite persons are sorted before non-favorite persons"; Photos 5+ only; returns 0 on Photos <= 4"""
return self._db._dbpersons_pk[self._pk]["manualorder"]
@cached_property
def feature_less(self):
"""Returns True if person has been marked as "Feature This Person Less" in Photos, False otherwise; Photos 8+ only; returns False on Photos <= 7"""
if self._db.photos_version < 8:
return False
if results := self._db.execute(
"""
SELECT ZTYPE
FROM ZUSERFEEDBACK
WHERE ZPERSON = ?
""",
(self._pk,),
).fetchone():
return bool(results[0])
return False
def asdict(self):
"""Returns dictionary representation of class instance"""
keyphoto = self.keyphoto.uuid if self.keyphoto is not None else None
@@ -82,6 +111,9 @@ class PersonInfo:
"keyface": self.keyface,
"facecount": self.facecount,
"keyphoto": keyphoto,
"favorite": self.favorite,
"sort_order": self.sort_order,
"feature_less": self.feature_less,
}
def json(self):

View File

@@ -1,6 +1,4 @@
"""
PhotoInfo class
Represents a single photo in the Photos library and provides access to the photo's attributes
""" PhotoInfo class: Represents a single photo in the Photos library and provides access to the photo's attributes
PhotosDB.photos() returns a list of PhotoInfo objects
"""
@@ -17,11 +15,13 @@ import pathlib
import plistlib
from datetime import timedelta, timezone
from functools import cached_property
from typing import Dict, Optional
from typing import Any, Dict, Optional
import yaml
from osxmetadata import OSXMetaData
import osxphotos
from ._constants import (
_DB_TABLE_NAMES,
_MOVIE_TYPE,
@@ -75,10 +75,10 @@ class PhotoInfo:
including keywords, persons, albums, uuid, path, etc.
"""
def __init__(self, db=None, uuid=None, info=None):
self._uuid = uuid
self._info = info
self._db = db
def __init__(self, db: "osxphotos.PhotosDB", uuid: str, info: dict[str, Any]):
self._uuid: str = uuid
self._info: dict[str, Any] = info
self._db: "osxphotos.PhotosDB" = db
self._verbose = self._db._verbose
@property

View File

@@ -3,18 +3,21 @@ PhotosDB class
Processes a Photos.app library database to extract information about photos
"""
from __future__ import annotations
import logging
import os
import os.path
import pathlib
import platform
import re
import sqlite3
import sys
import tempfile
from collections import OrderedDict
from collections.abc import Iterable
from datetime import datetime, timedelta, timezone
from typing import List, Optional
from typing import Any, List, Optional
from unicodedata import normalize
import bitmath
@@ -705,6 +708,8 @@ class PhotosDB:
"displayname": normalize_unicode(person[4]),
"photo_uuid": None,
"keyface_uuid": None,
"type": None, # Photos 5+
"manualorder": 0, # Photos 5+
}
try:
self._dbpersons_fullname[fullname].append(pk)
@@ -1687,7 +1692,9 @@ class PhotosDB:
ZPERSON.ZFULLNAME,
ZPERSON.ZFACECOUNT,
ZPERSON.ZKEYFACE,
ZPERSON.ZDISPLAYNAME
ZPERSON.ZDISPLAYNAME,
ZPERSON.ZTYPE,
ZPERSON.ZMANUALORDER
FROM ZPERSON
"""
)
@@ -1698,6 +1705,8 @@ class PhotosDB:
# 3 ZPERSON.ZFACECOUNT,
# 4 ZPERSON.ZKEYFACE,
# 5 ZPERSON.ZDISPLAYNAME
# 6 ZPERSON.ZTYPE, # ZTYPE = 1 == favorite, 0 == not favorite
# 7 ZPERSON.ZMANUALORDER # favorites are sorted by ZMANUALORDER
for person in c:
pk = person[0]
@@ -1715,6 +1724,8 @@ class PhotosDB:
"displayname": normalize_unicode(person[5]),
"photo_uuid": None,
"keyface_uuid": None,
"type": person[6],
"manualorder": person[7],
}
try:
self._dbpersons_fullname[fullname].append(pk)
@@ -3563,10 +3574,11 @@ class PhotosDB:
return photos
def execute(self, sql):
def execute(self, sql: str, params: Any | None = None) -> sqlite3.Cursor:
"""Execute sql statement and return cursor"""
self._db_connection, _ = self.get_db_connection()
return self._db_connection.cursor().execute(sql)
params = params or ()
return self._db_connection.cursor().execute(sql, params)
def _duplicate_signature(self, uuid):
"""Compute a signature for finding possible duplicates"""