CC set_* API

This commit is contained in:
Kovid Goyal 2013-07-17 16:24:42 +05:30
parent fb38f7d565
commit de5237c4d5
2 changed files with 116 additions and 5 deletions

View File

@ -441,26 +441,39 @@ class LibraryDatabase(object):
if notify: if notify:
self.notify('metadata', ids) 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) add = cleanup_tags(add)
remove = cleanup_tags(remove) remove = cleanup_tags(remove)
remove = set(remove) - set(add) remove = set(remove) - set(add)
if not ids or (not add and not remove): if not ids or (not add and not remove):
return return
remove = {icu_lower(x) for x in remove} remove = {icu_lower(x) for x in remove}
with self.new_api.write_lock: with self.new_api.write_lock:
val_map = {} val_map = {}
for book_id in ids: 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} existing = {icu_lower(x) for x in tags}
tags.extend(t for t in add if icu_lower(t) not in existing) 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) tags = tuple(t for t in tags if icu_lower(t) not in remove)
val_map[book_id] = tags 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: if notify:
self.notify('metadata', ids) 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): def unapply_tags(self, book_id, tags, notify=True):
self.bulk_modify_tags((book_id,), remove=tags, notify=notify) self.bulk_modify_tags((book_id,), remove=tags, notify=notify)
@ -589,6 +602,57 @@ class LibraryDatabase(object):
return [] return []
return list(self.new_api.remove_items(field, (item_id,))) 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 {{{ # Private interface {{{
def __iter__(self): def __iter__(self):
for row in self.data.iterall(): for row in self.data.iterall():
@ -777,3 +841,5 @@ LibraryDatabase.commit = MT(lambda self:None)
del MT del MT

View File

@ -48,10 +48,10 @@ def run_funcs(self, db, ndb, funcs):
meth(*args) meth(*args)
else: else:
fmt = lambda x:x fmt = lambda x:x
if meth[0] in {'!', '@', '#', '+', '$'}: if meth[0] in {'!', '@', '#', '+', '$', '-'}:
if meth[0] != '+': if meth[0] != '+':
fmt = {'!':dict, '@':lambda x:frozenset(x or ()), '#':lambda x:set((x or '').split(',')), 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: else:
fmt = args[-1] fmt = args[-1]
args = args[:-1] args = args[:-1]
@ -644,5 +644,50 @@ class LegacyTest(BaseTest):
for label in ('tags', 'authors', 'series'): for label in ('tags', 'authors', 'series'):
run_funcs(self, db, ndb, [('get_custom_and_extra', idx, label) for idx in range(3)]) run_funcs(self, db, ndb, [('get_custom_and_extra', idx, label) for idx in range(3)])
db.close() 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()
# }}} # }}}