STart work on write api for many-one fields

This commit is contained in:
Kovid Goyal 2013-02-27 17:34:37 +05:30
parent f3f4bdb023
commit 579142ee45
2 changed files with 91 additions and 4 deletions

View File

@ -22,6 +22,7 @@ from calibre.utils.localization import calibre_langcode_to_name
class Field(object): class Field(object):
is_many = False is_many = False
is_many_many = False
def __init__(self, name, table): def __init__(self, name, table):
self.name, self.table = name, table self.name, self.table = name, table
@ -299,6 +300,7 @@ class ManyToOneField(Field):
class ManyToManyField(Field): class ManyToManyField(Field):
is_many = True is_many = True
is_many_many = True
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
Field.__init__(self, *args, **kwargs) Field.__init__(self, *args, **kwargs)

View File

@ -128,8 +128,8 @@ def one_one_in_other(book_id_val_map, db, field, *args):
if deleted: if deleted:
db.conn.executemany('DELETE FROM %s WHERE book=?'%field.metadata['table'], db.conn.executemany('DELETE FROM %s WHERE book=?'%field.metadata['table'],
deleted) deleted)
for book_id in book_id_val_map: for book_id in deleted:
field.table.book_col_map.pop(book_id, None) field.table.book_col_map.pop(book_id[0], None)
updated = {k:v for k, v in book_id_val_map.iteritems() if v is not None} updated = {k:v for k, v in book_id_val_map.iteritems() if v is not None}
if updated: if updated:
db.conn.executemany('INSERT OR REPLACE INTO %s(book,%s) VALUES (?,?)'%( db.conn.executemany('INSERT OR REPLACE INTO %s(book,%s) VALUES (?,?)'%(
@ -154,6 +154,87 @@ def custom_series_index(book_id_val_map, db, field, *args):
return {s[0] for s in sequence} return {s[0] for s in sequence}
# }}} # }}}
# Many-One fields {{{
def many_one(book_id_val_map, db, field, allow_case_change, *args):
dirtied = set()
m = field.metadata
dt = m['datatype']
kmap = icu_lower if dt == 'text' else lambda x:x
rid_map = {kmap(v):k for k, v in field.table.id_map.iteritems()}
book_id_item_id_map = {k:rid_map.get(kmap(v), None) if v is not None else
None for k, v in book_id_val_map.iteritems()}
if allow_case_change:
for book_id, item_id in book_id_item_id_map.iteritems():
nval = book_id_val_map[book_id]
if (item_id is not None and nval != field.table.id_map[item_id]):
# Change of case
db.conn.execute('UPDATE %s SET %s=? WHERE id=?'%(
m['table'], m['column']), (nval, item_id))
field.table.id_map[item_id] = nval
dirtied |= field.table.col_book_map[item_id]
deleted = {k:v for k, v in book_id_val_map.iteritems() if v is None}
updated = {k:v for k, v in book_id_val_map.iteritems() if v is not None}
if deleted:
db.conn.executemany('DELETE FROM %s WHERE book=?'%m['link_table'],
tuple((book_id,) for book_id in deleted))
for book_id in deleted:
field.table.book_col_map.pop(book_id, None)
field.table.col_book_map.discard(book_id)
dirtied |= set(deleted)
if updated:
new_items = {k:v for k, v in updated.iteritems() if
book_id_item_id_map[k] is None}
changed_items = {k:book_id_item_id_map[k] for k in updated if
book_id_item_id_map[k] is not None}
def sql_update(imap):
db.conn.executemany(
'DELETE FROM {0} WHERE book=?; INSERT INTO {0}(book,{1}) VALUES(?, ?)'
.format(m['link_table'], m['link_column']),
tuple((book_id, book_id, item_id) for book_id, item_id in
imap.iteritems()))
if new_items:
imap = {}
for book_id, val in new_items.iteritems():
db.conn.execute('INSERT INTO %s(%s) VALUES (?)'%(
m['table'], m['column']), (val,))
imap[book_id] = item_id = db.conn.last_insert_rowid()
field.table.id_map[item_id] = val
field.table.col_book_map[item_id] = {book_id}
field.table.book_col_map[book_id] = item_id
sql_update(imap)
dirtied |= set(imap)
if changed_items:
imap = {}
sql_update(changed_items)
for book_id, item_id in changed_items.iteritems():
old_item_id = field.table.book_col_map[book_id]
if old_item_id != item_id:
field.table.book_col_map[book_id] = item_id
field.table.col_book_map[item_id].add(book_id)
field.table.col_book_map[old_item_id].discard(book_id)
imap[book_id] = item_id
sql_update(imap)
dirtied |= set(imap)
# Remove no longer used items
remove = {item_id for item_id, book_ids in
field.table.col_book_map.iteritems() if not book_ids}
if remove:
db.conn.executemany('DELETE FROM %s WHERE id=?'%m['table'],
tuple((item_id,) for item_id in remove))
for item_id in remove:
del field.table.id_map[item_id]
del field.table.col_book_map[item_id]
return dirtied
# }}}
def dummy(book_id_val_map, *args): def dummy(book_id_val_map, *args):
return set() return set()
@ -170,10 +251,13 @@ class Writer(object):
self.set_books_func = dummy self.set_books_func = dummy
elif self.name[0] == '#' and self.name.endswith('_index'): elif self.name[0] == '#' and self.name.endswith('_index'):
self.set_books_func = custom_series_index self.set_books_func = custom_series_index
elif field.is_many: elif field.is_many_many:
# TODO: Implement this # TODO: Implement this
pass pass
# TODO: Remember to change commas to | when writing authors to sqlite # TODO: Remember to change commas to | when writing authors to sqlite
elif field.is_many:
# TODO: Implement this
pass
else: else:
self.set_books_func = (one_one_in_books if field.metadata['table'] self.set_books_func = (one_one_in_books if field.metadata['table']
== 'books' else one_one_in_other) == 'books' else one_one_in_other)
@ -185,6 +269,7 @@ class Writer(object):
book_id_val_map.iteritems() if self.accept_vals(v)} book_id_val_map.iteritems() if self.accept_vals(v)}
if not book_id_val_map: if not book_id_val_map:
return set() return set()
dirtied = self.set_books_func(book_id_val_map, db, self.field) dirtied = self.set_books_func(book_id_val_map, db, self.field,
allow_case_change)
return dirtied return dirtied