diff --git a/src/calibre/db/tests/writing.py b/src/calibre/db/tests/writing.py index de40052c9b..ca8129f64c 100644 --- a/src/calibre/db/tests/writing.py +++ b/src/calibre/db/tests/writing.py @@ -266,7 +266,16 @@ class WritingTest(BaseTest): ae(sf('languages', {3:None}), set([3])) ae(cache.field_for('languages', 3), ()) - # TODO: identifiers + # Identifiers + f = cache.fields['identifiers'] + ae(sf('identifiers', {3: 'one:1,two:2'}), set([3])) + ae(sf('identifiers', {2:None}), set([2])) + ae(sf('identifiers', {1: {'test':'1', 'two':'2'}}), set([1])) + cache2 = self.init_cache(cl) + for c in (cache, cache2): + ae(c.field_for('identifiers', 3), {'one':'1', 'two':'2'}) + ae(c.field_for('identifiers', 2), {}) + ae(c.field_for('identifiers', 1), {'test':'1', 'two':'2'}) # }}} diff --git a/src/calibre/db/write.py b/src/calibre/db/write.py index 764be65900..8742c3e609 100644 --- a/src/calibre/db/write.py +++ b/src/calibre/db/write.py @@ -106,6 +106,21 @@ def adapt_languages(to_tuple, x): ans.append(lc) return tuple(ans) +def clean_identifier(typ, val): + typ = icu_lower(typ).strip().replace(':', '').replace(',', '') + val = val.strip().replace(',', '|').replace(':', '|') + return typ, val + +def adapt_identifiers(to_tuple, x): + if not isinstance(x, dict): + x = {k:v for k, v in (y.partition(':')[0::2] for y in to_tuple(x))} + ans = {} + for k, v in x.iteritems(): + k, v = clean_identifier(k, v) + if k and v: + ans[k] = v + return ans + def get_adapter(name, metadata): dt = metadata['datatype'] if dt == 'text': @@ -145,6 +160,8 @@ def get_adapter(name, metadata): return lambda x: 1.0 if ans(x) is None else ans(x) if name == 'languages': return partial(adapt_languages, ans) + if name == 'identifiers': + return partial(adapt_identifiers, ans) return ans # }}} @@ -396,6 +413,31 @@ def many_many(book_id_val_map, db, field, allow_case_change, *args): # }}} +def identifiers(book_id_val_map, db, field, *args): # {{{ + table = field.table + updates = set() + for book_id, identifiers in book_id_val_map.iteritems(): + if book_id not in table.book_col_map: + table.book_col_map[book_id] = {} + current_ids = table.book_col_map[book_id] + remove_keys = set(current_ids) - set(identifiers) + for key in remove_keys: + table.col_book_map.get(key, set()).discard(book_id) + current_ids.pop(key, None) + current_ids.update(identifiers) + for key, val in identifiers.iteritems(): + if key not in table.col_book_map: + table.col_book_map[key] = set() + table.col_book_map[key].add(book_id) + updates.add((book_id, key, val)) + db.conn.executemany('DELETE FROM identifiers WHERE book=?', + ((x,) for x in book_id_val_map)) + if updates: + db.conn.executemany('INSERT OR REPLACE INTO identifiers (book, type, val) VALUES (?, ?, ?)', + tuple(updates)) + return set(book_id_val_map) +# }}} + def dummy(book_id_val_map, *args): return set() @@ -412,6 +454,8 @@ class Writer(object): self.set_books_func = dummy elif self.name[0] == '#' and self.name.endswith('_index'): self.set_books_func = custom_series_index + elif self.name == 'identifiers': + self.set_books_func = identifiers elif field.is_many_many: self.set_books_func = many_many elif field.is_many: