Added compare for PhotoInfo objects, added loguru

This commit is contained in:
Rhet Turnbull
2019-06-15 07:49:57 -07:00
parent 72121f1d26
commit 4f0196e715

View File

@@ -17,7 +17,8 @@ from . import _applescript
# replace string formatting with fstrings # replace string formatting with fstrings
_debug = True _debug = False
def _get_os_version(): def _get_os_version():
# returns tuple containing OS version # returns tuple containing OS version
@@ -55,10 +56,10 @@ class PhotosDB:
print(dbfile) print(dbfile)
if dbfile is None: if dbfile is None:
library_path = self.get_photos_library_path() library_path = self.get_photos_library_path()
print("library_path: " + library_path) logger.debug("library_path: " + library_path)
# TODO: verify library path not None # TODO: verify library path not None
dbfile = os.path.join(library_path, "database/photos.db") dbfile = os.path.join(library_path, "database/photos.db")
print(dbfile) logger.debug(dbfile)
logger.debug("filename = %s" % dbfile) logger.debug("filename = %s" % dbfile)
@@ -93,15 +94,15 @@ class PhotosDB:
for k in self._dbfaces_person.keys(): for k in self._dbfaces_person.keys():
persons[k] = len(self._dbfaces_person[k]) persons[k] = len(self._dbfaces_person[k])
persons = dict(sorted(persons.items(), key=lambda kv: kv[1], reverse=True)) persons = dict(sorted(persons.items(), key=lambda kv: kv[1], reverse=True))
return persons return persons
def albums_as_dict(self): 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= {} albums = {}
for k in self._dbalbums_album.keys(): for k in self._dbalbums_album.keys():
albums[k] = len(self._dbalbums_album[k]) albums[k] = len(self._dbalbums_album[k])
albums = dict(sorted(albums.items(), key=lambda kv: kv[1], reverse=True)) albums = dict(sorted(albums.items(), key=lambda kv: kv[1], reverse=True))
return albums return albums
def keywords(self): def keywords(self):
# return list of keywords found in photos database # return list of keywords found in photos database
@@ -115,10 +116,9 @@ class PhotosDB:
def albums(self): def albums(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= self._dbalbums_album.keys() albums = self._dbalbums_album.keys()
return list(albums) return list(albums)
# Various AppleScripts we need # Various AppleScripts we need
def _setup_applescript(self): def _setup_applescript(self):
self._scpt_export = "" self._scpt_export = ""
@@ -252,7 +252,8 @@ class PhotosDB:
i = 0 i = 0
c.execute( c.execute(
"select count(*) from RKFace, RKPerson where RKFace.personID = RKperson.modelID" "select count(*) from RKFace, RKPerson, RKVersion where RKFace.personID = RKperson.modelID "
+ "and RKFace.imageModelId = RKVersion.modelId and RKVersion.isInTrash = 0"
) )
# init_pbar_status("Faces", c.fetchone()[0]) # init_pbar_status("Faces", c.fetchone()[0])
# c.execute("select RKPerson.name, RKFace.imageID from RKFace, RKPerson where RKFace.personID = RKperson.modelID") # c.execute("select RKPerson.name, RKFace.imageID from RKFace, RKPerson where RKFace.personID = RKperson.modelID")
@@ -260,7 +261,7 @@ class PhotosDB:
"select RKPerson.name, RKVersion.uuid from RKFace, RKPerson, RKVersion, RKMaster " "select RKPerson.name, RKVersion.uuid from RKFace, RKPerson, RKVersion, RKMaster "
+ "where RKFace.personID = RKperson.modelID and RKVersion.modelId = RKFace.ImageModelId " + "where RKFace.personID = RKperson.modelID and RKVersion.modelId = RKFace.ImageModelId "
+ "and RKVersion.type = 2 and RKVersion.masterUuid = RKMaster.uuid and " + "and RKVersion.type = 2 and RKVersion.masterUuid = RKMaster.uuid and "
+ "RKVersion.filename not like '%.pdf'" + "RKVersion.filename not like '%.pdf' and RKVersion.isInTrash = 0"
) )
for person in c: for person in c:
if person[0] == None: if person[0] == None:
@@ -472,74 +473,87 @@ class PhotosDB:
logger.debug(f"processed {len(self._dbphotos)} photos") logger.debug(f"processed {len(self._dbphotos)} photos")
def photos(self, keywords = [],uuid=[],persons=[],albums=[]): """
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)
"""
def photos(self, keywords=[], uuid=[], persons=[], albums=[]):
photos = [] photos = []
if not keywords and not uuid and not persons and not albums: if not keywords and not uuid and not persons and not albums:
#process all the photos # process all the photos
photos = list(self._dbphotos.keys()) photos = list(self._dbphotos.keys())
else: else:
if albums is not None: if albums is not None:
for album in albums: for album in albums:
print("album=%s" % album) logger.info("album=%s" % album)
if album in self._dbalbums_album: if album in self._dbalbums_album:
print("processing album %s:" % album) logger.info("processing album %s:" % album)
photos.extend(self._dbalbums_album[album]) photos.extend(self._dbalbums_album[album])
else: else:
print("Could not find album '%s' in database" % logger.debug(
(album), file=sys.stderr) "Could not find album '%s' in database" % (album),
)
if uuid is not None: if uuid is not None:
for u in uuid: for u in uuid:
print("uuid=%s" % u) logger.info("uuid=%s" % u)
if u in self._dbphotos: if u in self._dbphotos:
print("processing uuid %s:" % u) logger.info("processing uuid %s:" % u)
photos.extend([u]) photos.extend([u])
else: else:
print("Could not find uuid '%s' in database" % logger.debug(
(u), file=sys.stderr) "Could not find uuid '%s' in database" % (u),
)
if keywords is not None: if keywords is not None:
for keyword in keywords: for keyword in keywords:
print("keyword=%s" % keyword) logger.info("keyword=%s" % keyword)
if keyword in self._dbkeywords_keyword: if keyword in self._dbkeywords_keyword:
print("processing keyword %s:" % keyword) logger.info("processing keyword %s:" % keyword)
photos.extend(self._dbkeywords_keyword[keyword]) photos.extend(self._dbkeywords_keyword[keyword])
else: else:
print("Could not find keyword '%s' in database" % logger.debug(
(keyword), file=sys.stderr) "Could not find keyword '%s' in database" % (keyword),
)
if persons is not None: if persons is not None:
for person in persons: for person in persons:
print("person=%s" % person) logger.info("person=%s" % person)
if person in self._dbfaces_person: if person in self._dbfaces_person:
print("processing person %s:" % person) logger.info("processing person %s:" % person)
photos.extend(self._dbfaces_person[person]) photos.extend(self._dbfaces_person[person])
else: else:
print("Could not find person '%s' in database" % logger.debug(
(person), file=sys.stderr) "Could not find person '%s' in database" % (person),
)
photoinfo = [] photoinfo = []
for p in photos: for p in photos:
info = PhotoInfo(db = self, uuid = p, info = self._dbphotos[p]) logger.info(f"p={p}")
info = PhotoInfo(db=self, uuid=p, info=self._dbphotos[p])
photoinfo.append(info) photoinfo.append(info)
return photoinfo return photoinfo
""" """
Info about a specific photo, contains all the details we know about the photo Info about a specific photo, contains all the details we know about the photo
including keywords, persons, albums, uuid, path, etc. including keywords, persons, albums, uuid, path, etc.
""" """
class PhotoInfo():
def __init__(self, db = None, uuid = None, info = None):
class PhotoInfo:
def __init__(self, db=None, uuid=None, info=None):
self.uuid = uuid self.uuid = uuid
self.info = info self.info = info
self.db = db self.db = db
def filename(self): def filename(self):
return self.info['filename'] return self.info["filename"]
def date(self): def date(self):
return self.info['imageDate'] return self.info["imageDate"]
""" returns true if photo is missing from disk (which means it's not been downloaded from iCloud) """ returns true if photo is missing from disk (which means it's not been downloaded from iCloud)
NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos
@@ -550,39 +564,50 @@ class PhotoInfo():
downloaded from cloud to local storate their status in the database might still show downloaded from cloud to local storate their status in the database might still show
isMissing = 1 isMissing = 1
""" """
def ismissing(self): def ismissing(self):
return self.info['isMissing'] return self.info["isMissing"]
def path(self): def path(self):
photopath = "" photopath = ""
vol = self.info['volume'] vol = self.info["volume"]
if vol is not None: if vol is not None:
photopath = os.path.join('/Volumes', vol, self.info['imagePath']) photopath = os.path.join("/Volumes", vol, self.info["imagePath"])
else: else:
photopath = os.path.join(self.db._masters_path, self.info['imagePath']) photopath = os.path.join(self.db._masters_path, self.info["imagePath"])
if self.info['isMissing'] == 1: if self.info["isMissing"] == 1:
logger.warning(f"Skipping photo, not yet downloaded from iCloud: {photopath}") logger.warning(
f"Skipping photo, not yet downloaded from iCloud: {photopath}"
)
print(self.info) print(self.info)
photopath = None #path would be meaningless until downloaded photopath = None # path would be meaningless until downloaded
#TODO: Is there a way to use applescript to force the download in this # TODO: Is there a way to use applescript to force the download in this
return photopath return photopath
def description(self): def description(self):
return self.info['extendedDescription'] return self.info["extendedDescription"]
def persons(self): def persons(self):
return self.info['persons'] return self.info["persons"]
def albums(self): def albums(self):
return self.info['albums'] return self.info["albums"]
def keywords(self): def keywords(self):
return self.info['keywords'] return self.info["keywords"]
def name(self): def name(self):
return self.info['name'] return self.info["name"]
# compare two PhotoInfo objects for equality
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)