Have book_on_device() return the type of match and also the matched paths. Also clean it up and improve performance by no recompiling the regex for every book.

This commit is contained in:
Kovid Goyal 2010-09-09 17:08:01 -06:00
parent bc6e622d25
commit 8769c07451
2 changed files with 35 additions and 27 deletions

View File

@ -1308,35 +1308,43 @@ class DeviceMixin(object): # {{{
def book_on_device(self, id, format=None, reset=False): def book_on_device(self, id, format=None, reset=False):
''' '''
Return an indication of whether the given book represented by its db id Return an indication of whether the given book represented by its db id
is on the currently connected device. It returns a 4 element list. The is on the currently connected device. It returns a 6 element list. The
first three elements represent memory locations main, carda, and cardb, first three elements represent memory locations main, carda, and cardb,
and are true if the book is identifiably in that memory. The fourth and are true if the book is identifiably in that memory. The fourth
is the a count of how many instances of the book were found across all is a count of how many instances of the book were found across all
the memory locations. the memory locations. The fifth is the type of match. The type can be
one of: None, 'uuid', 'db_id', 'metadata'. The sixth is a set of paths to the
matching books on the device.
''' '''
loc = [None, None, None, 0] loc = [None, None, None, 0, None, set([])]
if reset: if reset:
self.book_db_title_cache = None self.book_db_title_cache = None
self.book_db_uuid_cache = None self.book_db_uuid_cache = None
self.book_db_id_counts = None self.book_db_id_counts = None
self.book_db_uuid_path_map = None
return return
string_pat = re.compile('(?u)\W|[_]')
def clean_string(x):
x = x.lower() if x else ''
return string_pat.sub('', x)
if self.book_db_title_cache is None: if self.book_db_title_cache is None:
self.book_db_title_cache = [] self.book_db_title_cache = []
self.book_db_uuid_cache = [] self.book_db_uuid_cache = []
self.book_db_uuid_path_map = {}
self.book_db_id_counts = {} self.book_db_id_counts = {}
for i, l in enumerate(self.booklists()): for i, l in enumerate(self.booklists()):
self.book_db_title_cache.append({}) self.book_db_title_cache.append({})
self.book_db_uuid_cache.append(set()) self.book_db_uuid_cache.append(set())
for book in l: for book in l:
book_title = book.title.lower() if book.title else '' book_title = clean_string(book.title)
book_title = re.sub('(?u)\W|[_]', '', book_title)
if book_title not in self.book_db_title_cache[i]: if book_title not in self.book_db_title_cache[i]:
self.book_db_title_cache[i][book_title] = \ self.book_db_title_cache[i][book_title] = \
{'authors':set(), 'db_ids':set(), 'uuids':set()} {'authors':set(), 'db_ids':set(),
book_authors = authors_to_string(book.authors).lower() 'uuids':set(), 'paths':set()}
book_authors = re.sub('(?u)\W|[_]', '', book_authors) book_authors = clean_string(authors_to_string(book.authors))
self.book_db_title_cache[i][book_title]['authors'].add(book_authors) self.book_db_title_cache[i][book_title]['authors'].add(book_authors)
db_id = getattr(book, 'application_id', None) db_id = getattr(book, 'application_id', None)
if db_id is None: if db_id is None:
@ -1350,35 +1358,36 @@ class DeviceMixin(object): # {{{
uuid = getattr(book, 'uuid', None) uuid = getattr(book, 'uuid', None)
if uuid is not None: if uuid is not None:
self.book_db_uuid_cache[i].add(uuid) self.book_db_uuid_cache[i].add(uuid)
self.book_db_uuid_path_map[uuid] = book.path
self.book_db_title_cache[i][book_title]['paths'].add(book.path)
mi = self.library_view.model().db.get_metadata(id, index_is_id=True) mi = self.library_view.model().db.get_metadata(id, index_is_id=True)
for i, l in enumerate(self.booklists()): for i, l in enumerate(self.booklists()):
if mi.uuid in self.book_db_uuid_cache[i]: if mi.uuid in self.book_db_uuid_cache[i]:
loc[i] = True loc[i] = True
loc[4] = 'uuid'
loc[5].add(self.book_db_uuid_path_map[mi.uuid])
continue continue
db_title = re.sub('(?u)\W|[_]', '', mi.title.lower()) db_title = clean_string(mi.title)
cache = self.book_db_title_cache[i].get(db_title, None) cache = self.book_db_title_cache[i].get(db_title, None)
if cache: if cache:
if id in cache['db_ids']: if id in cache['db_ids']:
loc[i] = True loc[i] = True
loc[4] = 'db_id'
loc[5] = cache['paths']
continue continue
if mi.authors and \ # Also check author sort, because it can be used as author in
re.sub('(?u)\W|[_]', '', authors_to_string(mi.authors).lower()) \ # some formats
in cache['authors']: if (mi.authors and clean_string(authors_to_string(mi.authors))
in cache['authors']) or (mi.author_sort and
clean_string(mi.author_sort) in cache['authors']):
# We really shouldn't get here, because set_books_in_library # We really shouldn't get here, because set_books_in_library
# should have set the db_ids for the books, and therefore # should have set the db_ids for the books, and therefore
# the if just above should have found them. Mark the book # the if just above should have found them. Mark the book
# anyway, and print a message about the situation # anyway, and print a message about the situation
loc[i] = True loc[i] = True
prints('book_on_device: matched title/author but not db_id!', loc[4] = 'metadata'
mi.title, authors_to_string(mi.authors)) loc[5] = cache['paths']
continue
# Also check author sort, because it can be used as author in
# some formats
if mi.author_sort and \
re.sub('(?u)\W|[_]', '', mi.author_sort.lower()) \
in cache['authors']:
loc[i] = True
continue continue
loc[3] = self.book_db_id_counts.get(id, 0) loc[3] = self.book_db_id_counts.get(id, 0)
return loc return loc
@ -1394,10 +1403,9 @@ class DeviceMixin(object): # {{{
if reset or not hasattr(self, 'db_book_title_cache'): if reset or not hasattr(self, 'db_book_title_cache'):
# It might be possible to get here without having initialized the # It might be possible to get here without having initialized the
# library view. In this case, simply give up # library view. In this case, simply give up
if not hasattr(self, 'library_view') or self.library_view is None: try:
return db = self.library_view.model().db
db = getattr(self.library_view.model(), 'db', None) except:
if db is None:
return return
# Build a cache (map) of the library, so the search isn't On**2 # Build a cache (map) of the library, so the search isn't On**2
self.db_book_title_cache = {} self.db_book_title_cache = {}

View File

@ -641,7 +641,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
count = 0 count = 0
on = self.book_on_device(id) on = self.book_on_device(id)
if on is not None: if on is not None:
m, a, b, count = on m, a, b, count = on[:4]
if m is not None: if m is not None:
loc.append(_('Main')) loc.append(_('Main'))
if a is not None: if a is not None: