mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement custom book data API
This commit is contained in:
parent
90599f9c46
commit
6c9a6c1020
@ -1175,5 +1175,46 @@ class DB(object):
|
|||||||
self.rmtree(parent, permanent=permanent)
|
self.rmtree(parent, permanent=permanent)
|
||||||
self.conn.executemany(
|
self.conn.executemany(
|
||||||
'DELETE FROM books WHERE id=?', [(x,) for x in path_map])
|
'DELETE FROM books WHERE id=?', [(x,) for x in path_map])
|
||||||
|
|
||||||
|
def add_custom_data(self, name, val_map, delete_first):
|
||||||
|
if delete_first:
|
||||||
|
self.conn.execute('DELETE FROM books_plugin_data WHERE name=?', (name, ))
|
||||||
|
self.conn.executemany(
|
||||||
|
'INSERT OR REPLACE INTO books_plugin_data (book, name, val) VALUES (?, ?, ?)',
|
||||||
|
[(book_id, name, json.dumps(val, default=to_json))
|
||||||
|
for book_id, val in val_map.iteritems()])
|
||||||
|
|
||||||
|
def get_custom_book_data(self, name, book_ids, default=None):
|
||||||
|
book_ids = frozenset(book_ids)
|
||||||
|
def safe_load(val):
|
||||||
|
try:
|
||||||
|
return json.loads(val, object_hook=from_json)
|
||||||
|
except:
|
||||||
|
return default
|
||||||
|
|
||||||
|
if len(book_ids) == 1:
|
||||||
|
bid = next(iter(book_ids))
|
||||||
|
ans = {book_id:safe_load(val) for book_id, val in
|
||||||
|
self.conn.execute('SELECT book, val FROM books_plugin_data WHERE book=? AND name=?', (bid, name))}
|
||||||
|
return ans or {bid:default}
|
||||||
|
|
||||||
|
ans = {}
|
||||||
|
for book_id, val in self.conn.execute(
|
||||||
|
'SELECT book, val FROM books_plugin_data WHERE name=?', (name,)):
|
||||||
|
if not book_ids or book_id in book_ids:
|
||||||
|
val = safe_load(val)
|
||||||
|
ans[book_id] = val
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def delete_custom_book_data(self, name, book_ids):
|
||||||
|
if book_ids:
|
||||||
|
self.conn.executemany('DELETE FROM books_plugin_data WHERE book=? AND name=?',
|
||||||
|
[(book_id, name) for book_id in book_ids])
|
||||||
|
else:
|
||||||
|
self.conn.execute('DELETE FROM books_plugin_data WHERE name=?', (name,))
|
||||||
|
|
||||||
|
def get_ids_for_custom_book_data(self, name):
|
||||||
|
return frozenset(r[0] for r in self.conn.execute('SELECT book FROM books_plugin_data WHERE name=?', (name,)))
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -1147,6 +1147,36 @@ class Cache(object):
|
|||||||
else:
|
else:
|
||||||
table.remove_books(book_ids, self.backend)
|
table.remove_books(book_ids, self.backend)
|
||||||
|
|
||||||
|
@write_api
|
||||||
|
def add_custom_book_data(self, name, val_map, delete_first=False):
|
||||||
|
''' Add data for name where val_map is a map of book_ids to values. If
|
||||||
|
delete_first is True, all previously stored data for name will be
|
||||||
|
removed. '''
|
||||||
|
missing = frozenset(val_map) - self._all_book_ids()
|
||||||
|
if missing:
|
||||||
|
raise ValueError('add_custom_book_data: no such book_ids: %d'%missing)
|
||||||
|
self.backend.add_custom_data(name, val_map, delete_first)
|
||||||
|
|
||||||
|
@read_api
|
||||||
|
def get_custom_book_data(self, name, book_ids=(), default=None):
|
||||||
|
''' Get data for name. By default returns data for all book_ids, pass
|
||||||
|
in a list of book ids if you only want some data. Returns a map of
|
||||||
|
book_id to values. If a particular value could not be decoded, uses
|
||||||
|
default for it. '''
|
||||||
|
return self.backend.get_custom_book_data(name, book_ids, default)
|
||||||
|
|
||||||
|
@write_api
|
||||||
|
def delete_custom_book_data(self, name, book_ids=()):
|
||||||
|
''' Delete data for name. By default deletes all data, if you only want
|
||||||
|
to delete data for some book ids, pass in a list of book ids. '''
|
||||||
|
self.backend.delete_custom_book_data(name, book_ids)
|
||||||
|
|
||||||
|
@read_api
|
||||||
|
def get_ids_for_custom_book_data(self, name):
|
||||||
|
''' Return the set of book ids for which name has data. '''
|
||||||
|
return self.backend.get_ids_for_custom_book_data(name)
|
||||||
|
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class SortKey(object): # {{{
|
class SortKey(object): # {{{
|
||||||
|
@ -216,6 +216,29 @@ class LibraryDatabase(object):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
# Custom data {{{
|
||||||
|
def add_custom_book_data(self, book_id, name, val):
|
||||||
|
self.new_api.add_custom_book_data(name, {book_id:val})
|
||||||
|
|
||||||
|
def add_multiple_custom_book_data(self, name, val_map, delete_first=False):
|
||||||
|
self.new_api.add_custom_book_data(name, val_map, delete_first=delete_first)
|
||||||
|
|
||||||
|
def get_custom_book_data(self, book_id, name, default=None):
|
||||||
|
return self.new_api.get_custom_book_data(name, book_ids={book_id}, default=default).get(book_id, default)
|
||||||
|
|
||||||
|
def get_all_custom_book_data(self, name, default=None):
|
||||||
|
return self.new_api.get_custom_book_data(name, default=default)
|
||||||
|
|
||||||
|
def delete_custom_book_data(self, book_id, name):
|
||||||
|
self.new_api.delete_custom_book_data(name, book_ids=(book_id,))
|
||||||
|
|
||||||
|
def delete_all_custom_book_data(self, name):
|
||||||
|
self.new_api.delete_custom_book_data(name)
|
||||||
|
|
||||||
|
def get_ids_for_custom_book_data(self, name):
|
||||||
|
return list(self.new_api.get_ids_for_custom_book_data(name))
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Private interface {{{
|
# Private interface {{{
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
@ -245,3 +245,34 @@ class LegacyTest(BaseTest):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
def test_legacy_custom_data(self): # {{{
|
||||||
|
'Test the API for custom data storage'
|
||||||
|
legacy, old = self.init_legacy(self.cloned_library), self.init_old(self.cloned_library)
|
||||||
|
for name in ('name1', 'name2', 'name3'):
|
||||||
|
T = partial(ET, 'add_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T((1, name, 'val1'))(self)
|
||||||
|
T((2, name, 'val2'))(self)
|
||||||
|
T((3, name, 'val3'))(self)
|
||||||
|
T = partial(ET, 'get_ids_for_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T((name,))(self)
|
||||||
|
T = partial(ET, 'get_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T((1, name, object()))
|
||||||
|
T((9, name, object()))
|
||||||
|
T = partial(ET, 'get_all_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T((name, object()))
|
||||||
|
T((name+'!', object()))
|
||||||
|
T = partial(ET, 'delete_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T((name, 1))
|
||||||
|
T = partial(ET, 'get_all_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T((name, object()))
|
||||||
|
T = partial(ET, 'delete_all_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T((name))
|
||||||
|
T = partial(ET, 'get_all_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T((name, object()))
|
||||||
|
|
||||||
|
T = partial(ET, 'add_multiple_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T(('n', {1:'val1', 2:'val2'}))(self)
|
||||||
|
T = partial(ET, 'get_all_custom_book_data', old=old, legacy=legacy)
|
||||||
|
T(('n', object()))
|
||||||
|
old.close()
|
||||||
|
# }}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user