mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
CC set_* API
This commit is contained in:
parent
fb38f7d565
commit
de5237c4d5
@ -441,26 +441,39 @@ class LibraryDatabase(object):
|
||||
if notify:
|
||||
self.notify('metadata', ids)
|
||||
|
||||
def bulk_modify_tags(self, ids, add=[], remove=[], notify=False):
|
||||
def _do_bulk_modify(self, field, ids, add, remove, notify):
|
||||
add = cleanup_tags(add)
|
||||
remove = cleanup_tags(remove)
|
||||
remove = set(remove) - set(add)
|
||||
if not ids or (not add and not remove):
|
||||
return
|
||||
|
||||
remove = {icu_lower(x) for x in remove}
|
||||
with self.new_api.write_lock:
|
||||
val_map = {}
|
||||
for book_id in ids:
|
||||
tags = list(self.new_api._field_for('tags', book_id))
|
||||
tags = list(self.new_api._field_for(field, book_id))
|
||||
existing = {icu_lower(x) for x in tags}
|
||||
tags.extend(t for t in add if icu_lower(t) not in existing)
|
||||
tags = tuple(t for t in tags if icu_lower(t) not in remove)
|
||||
val_map[book_id] = tags
|
||||
self.new_api._set_field('tags', val_map, allow_case_change=False)
|
||||
self.new_api._set_field(field, val_map, allow_case_change=False)
|
||||
|
||||
if notify:
|
||||
self.notify('metadata', ids)
|
||||
|
||||
def bulk_modify_tags(self, ids, add=[], remove=[], notify=False):
|
||||
self._do_bulk_modify('tags', ids, add, remove, notify)
|
||||
|
||||
def set_custom_bulk_multiple(self, ids, add=[], remove=[], label=None, num=None, notify=False):
|
||||
data = self.backend.custom_field_metadata(label, num)
|
||||
if not data['editable']:
|
||||
raise ValueError('Column %r is not editable'%data['label'])
|
||||
if data['datatype'] != 'text' or not data['is_multiple']:
|
||||
raise ValueError('Column %r is not text/multiple'%data['label'])
|
||||
field = self.custom_field_name(label, num)
|
||||
self._do_bulk_modify(field, ids, add, remove, notify)
|
||||
|
||||
def unapply_tags(self, book_id, tags, notify=True):
|
||||
self.bulk_modify_tags((book_id,), remove=tags, notify=notify)
|
||||
|
||||
@ -589,6 +602,57 @@ class LibraryDatabase(object):
|
||||
return []
|
||||
return list(self.new_api.remove_items(field, (item_id,)))
|
||||
|
||||
def set_custom(self, book_id, val, label=None, num=None, append=False,
|
||||
notify=True, extra=None, commit=True, allow_case_change=False):
|
||||
field = self.custom_field_name(label, num)
|
||||
data = self.backend.custom_field_metadata(label, num)
|
||||
if data['datatype'] == 'composite':
|
||||
return set()
|
||||
if not data['editable']:
|
||||
raise ValueError('Column %r is not editable'%data['label'])
|
||||
if data['datatype'] == 'enumeration' and (
|
||||
val and val not in data['display']['enum_values']):
|
||||
return set()
|
||||
with self.new_api.write_lock:
|
||||
if append and data['is_multiple']:
|
||||
current = self.new_api._field_for(field, book_id)
|
||||
existing = {icu_lower(x) for x in current}
|
||||
val = current + tuple(x for x in self.new_api.fields[field].writer.adapter(val) if icu_lower(x) not in existing)
|
||||
affected_books = self.new_api._set_field(field, {book_id:val}, allow_case_change=allow_case_change)
|
||||
else:
|
||||
affected_books = self.new_api._set_field(field, {book_id:val}, allow_case_change=allow_case_change)
|
||||
if data['datatype'] == 'series':
|
||||
extra = 1.0 if extra is None else extra
|
||||
self.new_api._set_field(field + '_index', {book_id:extra})
|
||||
if notify and affected_books:
|
||||
self.notify('metadata', list(affected_books))
|
||||
return affected_books
|
||||
|
||||
def set_custom_bulk(self, ids, val, label=None, num=None,
|
||||
append=False, notify=True, extras=None):
|
||||
if extras is not None and len(extras) != len(ids):
|
||||
raise ValueError('Length of ids and extras is not the same')
|
||||
field = self.custom_field_name(label, num)
|
||||
data = self.backend.custom_field_metadata(label, num)
|
||||
if data['datatype'] == 'composite':
|
||||
return set()
|
||||
if data['datatype'] == 'enumeration' and (
|
||||
val and val not in data['display']['enum_values']):
|
||||
return
|
||||
if not data['editable']:
|
||||
raise ValueError('Column %r is not editable'%data['label'])
|
||||
|
||||
if append:
|
||||
for book_id in ids:
|
||||
self.set_custom(book_id, val, label=label, num=num, append=True, notify=False)
|
||||
else:
|
||||
with self.new_api.write_lock:
|
||||
self.new_api._set_field(field, {book_id:val for book_id in ids}, allow_case_change=False)
|
||||
if extras is not None:
|
||||
self.new_api._set_field(field + '_index', {book_id:val for book_id, val in zip(ids, extras)})
|
||||
if notify:
|
||||
self.notify('metadata', list(ids))
|
||||
|
||||
# Private interface {{{
|
||||
def __iter__(self):
|
||||
for row in self.data.iterall():
|
||||
@ -777,3 +841,5 @@ LibraryDatabase.commit = MT(lambda self:None)
|
||||
del MT
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -48,10 +48,10 @@ def run_funcs(self, db, ndb, funcs):
|
||||
meth(*args)
|
||||
else:
|
||||
fmt = lambda x:x
|
||||
if meth[0] in {'!', '@', '#', '+', '$'}:
|
||||
if meth[0] in {'!', '@', '#', '+', '$', '-'}:
|
||||
if meth[0] != '+':
|
||||
fmt = {'!':dict, '@':lambda x:frozenset(x or ()), '#':lambda x:set((x or '').split(',')),
|
||||
'$':lambda x:set(tuple(y) for y in x)}[meth[0]]
|
||||
'$':lambda x:set(tuple(y) for y in x), '-':lambda x:None}[meth[0]]
|
||||
else:
|
||||
fmt = args[-1]
|
||||
args = args[:-1]
|
||||
@ -644,5 +644,50 @@ class LegacyTest(BaseTest):
|
||||
for label in ('tags', 'authors', 'series'):
|
||||
run_funcs(self, db, ndb, [('get_custom_and_extra', idx, label) for idx in range(3)])
|
||||
db.close()
|
||||
|
||||
ndb = self.init_legacy(self.cloned_library)
|
||||
db = self.init_old(self.cloned_library)
|
||||
# Test setting
|
||||
run_funcs(self, db, ndb, (
|
||||
('-set_custom', 1, 't1 & t2', 'authors'),
|
||||
('-set_custom', 1, 't3 & t4', 'authors', None, True),
|
||||
('-set_custom', 3, 'test one & test Two', 'authors'),
|
||||
('-set_custom', 1, 'ijfkghkjdf', 'enum'),
|
||||
('-set_custom', 3, 'One', 'enum'),
|
||||
('-set_custom', 3, 'xxx', 'formats'),
|
||||
('-set_custom', 1, 'my tag two', 'tags', None, False, False, None, True, True),
|
||||
(db.clean,), (db.refresh,),
|
||||
('all_custom', 'series'), ('all_custom', 'tags'), ('all_custom', 'authors'),
|
||||
))
|
||||
for label in ('tags', 'series', 'authors', 'comments', 'rating', 'date', 'yesno', 'isbn', 'enum', 'formats', 'float', 'comp_tags'):
|
||||
for func in ('get_custom', 'get_custom_extra', 'get_custom_and_extra'):
|
||||
run_funcs(self, db, ndb, [(func, idx, label) for idx in range(3)])
|
||||
db.close()
|
||||
|
||||
ndb = self.init_legacy(self.cloned_library)
|
||||
db = self.init_old(self.cloned_library)
|
||||
# Test setting bulk
|
||||
run_funcs(self, db, ndb, (
|
||||
('set_custom_bulk', (1,2,3), 't1 & t2', 'authors'),
|
||||
('set_custom_bulk', (1,2,3), 'a series', 'series', None, False, False, (9, 10, 11)),
|
||||
('set_custom_bulk', (1,2,3), 't1', 'tags', None, True),
|
||||
(db.clean,), (db.refresh,),
|
||||
('all_custom', 'series'), ('all_custom', 'tags'), ('all_custom', 'authors'),
|
||||
))
|
||||
for label in ('tags', 'series', 'authors', 'comments', 'rating', 'date', 'yesno', 'isbn', 'enum', 'formats', 'float', 'comp_tags'):
|
||||
for func in ('get_custom', 'get_custom_extra', 'get_custom_and_extra'):
|
||||
run_funcs(self, db, ndb, [(func, idx, label) for idx in range(3)])
|
||||
db.close()
|
||||
|
||||
ndb = self.init_legacy(self.cloned_library)
|
||||
db = self.init_old(self.cloned_library)
|
||||
# Test bulk multiple
|
||||
run_funcs(self, db, ndb, (
|
||||
('set_custom_bulk_multiple', (1,2,3), ['t1'], ['My Tag One'], 'tags'),
|
||||
(db.clean,), (db.refresh,),
|
||||
('all_custom', 'tags'),
|
||||
('get_custom', 0, 'tags'), ('get_custom', 1, 'tags'), ('get_custom', 2, 'tags'),
|
||||
))
|
||||
db.close()
|
||||
# }}}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user