Track creation and modified timestamps for notes

This commit is contained in:
Kovid Goyal 2023-09-13 12:39:28 +05:30
parent 652fb651cb
commit b0766d7a62
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 34 additions and 9 deletions

View File

@ -3,6 +3,8 @@ CREATE TABLE notes_db.notes ( id INTEGER PRIMARY KEY AUTOINCREMENT,
colname TEXT NOT NULL COLLATE NOCASE,
doc TEXT NOT NULL DEFAULT '',
searchable_text TEXT NOT NULL DEFAULT '',
ctime REAL DEFAULT (unixepoch('subsec')),
mtime REAL DEFAULT (unixepoch('subsec')),
UNIQUE(item, colname)
);
@ -41,6 +43,7 @@ BEGIN
INSERT INTO notes_fts(rowid, searchable_text) VALUES (NEW.id, NEW.searchable_text);
INSERT INTO notes_fts_stemmed(notes_fts_stemmed, rowid, searchable_text) VALUES('delete', OLD.id, OLD.searchable_text);
INSERT INTO notes_fts_stemmed(rowid, searchable_text) VALUES (NEW.id, NEW.searchable_text);
UPDATE notes SET mtime=unixepoch('subsec') WHERE id = OLD.id;
END;
CREATE TRIGGER notes_db.notes_db_resources_delete_trg BEFORE DELETE ON notes_db.resources

View File

@ -205,7 +205,10 @@ class Notes:
srcdir = self.path_to_retired_dir_for_item(field_name, item_id, item_value)
remove_with_retry(srcdir, is_dir=True)
def set_note(self, conn, field_name, item_id, item_value, marked_up_text='', used_resource_hashes=(), searchable_text=copy_marked_up_text):
def set_note(
self, conn, field_name, item_id, item_value, marked_up_text='', used_resource_hashes=(),
searchable_text=copy_marked_up_text, ctime=None, mtime=None
):
if searchable_text is copy_marked_up_text:
searchable_text = marked_up_text
searchable_text = item_value + '\n' + searchable_text
@ -228,9 +231,19 @@ class Notes:
resources_to_add = new_resources - old_resources
if note_id is None:
self.remove_retired_entry(field_name, item_id, item_value)
note_id = conn.get('''
INSERT INTO notes_db.notes (item,colname,doc,searchable_text) VALUES (?,?,?,?) RETURNING notes.id;
''', (item_id, field_name, marked_up_text, searchable_text), all=False)
if ctime is not None or mtime is not None:
now = time.time()
if ctime is None:
ctime = now
if mtime is None:
mtime = now
note_id = conn.get('''
INSERT INTO notes_db.notes (item,colname,doc,searchable_text,ctime,mtime) VALUES (?,?,?,?,?,?) RETURNING notes.id;
''', (item_id, field_name, marked_up_text, searchable_text, ctime, mtime), all=False)
else:
note_id = conn.get('''
INSERT INTO notes_db.notes (item,colname,doc,searchable_text) VALUES (?,?,?,?) RETURNING notes.id;
''', (item_id, field_name, marked_up_text, searchable_text), all=False)
else:
conn.execute('UPDATE notes_db.notes SET doc=?,searchable_text=? WHERE id=?', (marked_up_text, searchable_text, note_id))
if resources_to_potentially_remove:
@ -246,11 +259,12 @@ class Notes:
return conn.get('SELECT doc FROM notes_db.notes WHERE item=? AND colname=?', (item_id, field_name), all=False)
def get_note_data(self, conn, field_name, item_id):
for (note_id, doc, searchable_text) in conn.execute(
'SELECT id,doc,searchable_text FROM notes_db.notes WHERE item=? AND colname=?', (item_id, field_name)
for (note_id, doc, searchable_text, ctime, mtime) in conn.execute(
'SELECT id,doc,searchable_text,ctime,mtime FROM notes_db.notes WHERE item=? AND colname=?', (item_id, field_name)
):
return {
'id': note_id, 'doc': doc, 'searchable_text': searchable_text,
'ctime': ctime, 'mtime': mtime,
'resource_hashes': frozenset(self.resources_used_by(conn, note_id)),
}
@ -427,6 +441,7 @@ class Notes:
try:
with open(make_long_path_useable(os.path.join(self.backup_dir, field, str(item_id)))) as f:
raw = f.read()
st = os.stat(f.fileno())
except OSError as e:
errors.append(_('Failed to read from document for {path} with error: {error}').format(path=item_val, error=e))
continue
@ -441,7 +456,7 @@ class Notes:
errors.append(_('Some resources for {} were missing').format(item_val))
resources &= known_resources
try:
self.set_note(conn, field, item_id, item_val, doc, resources, searchable_text)
self.set_note(conn, field, item_id, item_val, doc, resources, searchable_text, ctime=st.st_ctime, mtime=st.st_mtime)
except Exception as e:
errors.append(_('Failed to set note for {path} with error: {error}').format(path=item_val, error=e))
else:

View File

@ -2,7 +2,7 @@
# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>
import os
import os, time
from calibre.db.tests.base import BaseTest
@ -75,7 +75,14 @@ def test_cache_api(self: 'NotesTest'):
h2 = cache.add_notes_resource(b'resource2', 'r1.jpg')
cache.set_notes_for('authors', author_id, doc, resource_hashes=(h1, h2))
nd = cache.notes_data_for('authors', author_id)
self.ae(nd, {'id': 1, 'searchable_text': authors[0] + '\n' + doc, 'doc': doc, 'resource_hashes': frozenset({h1, h2})})
self.ae(nd, {'id': 1, 'ctime': nd['ctime'], 'mtime': nd['ctime'], 'searchable_text': authors[0] + '\n' + doc,
'doc': doc, 'resource_hashes': frozenset({h1, h2})})
time.sleep(0.01)
cache.set_notes_for('authors', author_id, doc, resource_hashes=(h1, h2))
n2d = cache.notes_data_for('authors', author_id)
self.ae(nd['ctime'], n2d['ctime'])
self.assertGreater(n2d['mtime'], nd['mtime'])
# test renaming to a new author preserves notes
cache.rename_items('authors', {author_id: 'renamed author'})
raid = cache.get_item_id('authors', 'renamed author')