Implement unretiring of notes when setting values

This commit is contained in:
Kovid Goyal 2023-08-18 10:55:39 +05:30
parent 1ae94801e6
commit 53bfdd6ecf
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 20 additions and 6 deletions

View File

@ -991,6 +991,9 @@ class DB:
if note_id is not None: if note_id is not None:
yield from self.notes.resources_used_by(conn, note_id) yield from self.notes.resources_used_by(conn, note_id)
def unretire_note(self, field, item_id, item_val):
return self.notes.unretire(self.conn, field, item_id, item_val)
def initialize_fts(self, dbref): def initialize_fts(self, dbref):
self.fts = None self.fts = None
if not self.prefs['fts_enabled']: if not self.prefs['fts_enabled']:

View File

@ -13,6 +13,7 @@ from typing import Optional, Union
from calibre.constants import iswindows from calibre.constants import iswindows
from calibre.utils.copy_files import WINDOWS_SLEEP_FOR_RETRY_TIME from calibre.utils.copy_files import WINDOWS_SLEEP_FOR_RETRY_TIME
from calibre.utils.filenames import copyfile_using_links, make_long_path_useable from calibre.utils.filenames import copyfile_using_links, make_long_path_useable
from calibre.utils.icu import lower as icu_lower
from ..constants import NOTES_DIR_NAME from ..constants import NOTES_DIR_NAME
from .schema_upgrade import SchemaUpgrade from .schema_upgrade import SchemaUpgrade
@ -130,7 +131,7 @@ class Notes:
def retire_entry(self, field_name, item_id, item_value, resources, note_id): def retire_entry(self, field_name, item_id, item_value, resources, note_id):
path = make_long_path_useable(os.path.join(self.backup_dir, field_name, str(item_id))) path = make_long_path_useable(os.path.join(self.backup_dir, field_name, str(item_id)))
if os.path.exists(path): if os.path.exists(path):
key = (item_value or '').lower() key = icu_lower(item_value or '')
destdir = os.path.join(self.retired_dir, hash_key(f'{field_name} {key}')) destdir = os.path.join(self.retired_dir, hash_key(f'{field_name} {key}'))
os.makedirs(make_long_path_useable(destdir), exist_ok=True) os.makedirs(make_long_path_useable(destdir), exist_ok=True)
dest = os.path.join(destdir, DOC_NAME) dest = os.path.join(destdir, DOC_NAME)
@ -145,7 +146,7 @@ class Notes:
self.trim_retired_dir() self.trim_retired_dir()
def unretire(self, conn, field_name, item_id, item_value) -> int: def unretire(self, conn, field_name, item_id, item_value) -> int:
key = (item_value or '').lower() key = icu_lower(item_value or '')
srcdir = make_long_path_useable(os.path.join(self.retired_dir, hash_key(f'{field_name} {key}'))) srcdir = make_long_path_useable(os.path.join(self.retired_dir, hash_key(f'{field_name} {key}')))
note_id = -1 note_id = -1
if not os.path.exists(srcdir) or self.note_id_for(conn, field_name, item_id) is not None: if not os.path.exists(srcdir) or self.note_id_for(conn, field_name, item_id) is not None:

View File

@ -79,8 +79,14 @@ def test_cache_api(self):
# test removing author from db retires notes # test removing author from db retires notes
cache.set_field('authors', {bid:('New Author',) for bid in cache.all_book_ids()}) cache.set_field('authors', {bid:('New Author',) for bid in cache.all_book_ids()})
self.ae(len(cache.all_field_ids('authors')), 1) self.ae(len(cache.all_field_ids('authors')), 1)
before = os.listdir(notes.retired_dir) self.ae(len(os.listdir(notes.retired_dir)), 1)
self.ae(len(before), 1) # test re-using of retired note
cache.set_field('authors', {1:'Author One'})
author_id = cache.get_item_id('authors', 'Author One')
self.ae(cache.notes_resources_used_by('authors', author_id), frozenset({h1, h2}))
self.ae(cache.get_notes_resource(h1)['data'], b'resource1')
self.ae(cache.get_notes_resource(h2)['data'], b'resource2')
self.assertFalse(os.listdir(notes.retired_dir))
class NotesTest(BaseTest): class NotesTest(BaseTest):

View File

@ -297,6 +297,8 @@ def get_db_id(val, db, m, table, kmap, rid_map, allow_case_change,
table.asort_map[item_id] = aus table.asort_map[item_id] = aus
if hasattr(table, 'link_map'): if hasattr(table, 'link_map'):
table.link_map[item_id] = '' table.link_map[item_id] = ''
if table.supports_notes and isinstance(val, str):
db.unretire_note(table.name, item_id, val)
elif allow_case_change and val != table.id_map[item_id]: elif allow_case_change and val != table.id_map[item_id]:
case_changes[item_id] = val case_changes[item_id] = val
val_map[val] = item_id val_map[val] = item_id
@ -380,6 +382,7 @@ def many_one(book_id_val_map, db, field, allow_case_change, *args):
# Remove no longer used items # Remove no longer used items
remove = {item_id:item_val for item_id, item_val in table.id_map.items() if not table.col_book_map.get(item_id, False)} remove = {item_id:item_val for item_id, item_val in table.id_map.items() if not table.col_book_map.get(item_id, False)}
if remove: if remove:
if table.supports_notes:
db.clear_notes_for_category_items(table.name, remove) db.clear_notes_for_category_items(table.name, remove)
db.executemany('DELETE FROM %s WHERE id=?'%m['table'], db.executemany('DELETE FROM %s WHERE id=?'%m['table'],
((item_id,) for item_id in remove)) ((item_id,) for item_id in remove))
@ -484,6 +487,7 @@ def many_many(book_id_val_map, db, field, allow_case_change, *args):
# Remove no longer used items # Remove no longer used items
remove = {item_id:item_val for item_id, item_val in table.id_map.items() if not table.col_book_map.get(item_id, False)} remove = {item_id:item_val for item_id, item_val in table.id_map.items() if not table.col_book_map.get(item_id, False)}
if remove: if remove:
if table.supports_notes:
db.clear_notes_for_category_items(table.name, remove) db.clear_notes_for_category_items(table.name, remove)
db.executemany('DELETE FROM %s WHERE id=?'%m['table'], db.executemany('DELETE FROM %s WHERE id=?'%m['table'],
((item_id,) for item_id in remove)) ((item_id,) for item_id in remove))