Annots browser: Allow browsing upto 4096 annotations with no search query

This commit is contained in:
Kovid Goyal 2020-07-09 16:43:50 +05:30
parent 1f3b5993b7
commit 21f3348ab7
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 81 additions and 27 deletions

View File

@ -1820,12 +1820,49 @@ class DB(object):
raise FTSQueryError(fts_engine_query, query, e)
def all_annotations_for_book(self, book_id):
for (fmt, user_type, user, data) in self.execute('SELECT format, user_type, user, annot_data FROM annotations WHERE book=?', (book_id,)):
for (fmt, user_type, user, data) in self.execute('SELECT id, book, format, user_type, user, annot_data FROM annotations WHERE book=?', (book_id,)):
try:
yield {'format': fmt, 'user_type': user_type, 'user': user, 'annotation': json.loads(data)}
except Exception:
pass
def all_annotations(self, restrict_to_user=None, limit=None, annotation_type=None):
ls = json.loads
q = 'SELECT id, book, format, user_type, user, annot_data FROM annotations'
data = []
if restrict_to_user or annotation_type:
q += ' WHERE '
if restrict_to_user is not None:
data.extend(restrict_to_user)
q += ' user_type = ? AND user = ?'
if annotation_type:
data.append(annotation_type)
q += ' annot_type = ? '
q += ' ORDER BY timestamp'
if limit is not None:
q += ' LIMIT %d' % limit
for (rowid, book_id, fmt, user_type, user, annot_data) in self.execute(q, tuple(data)):
try:
annot = ls(annot_data)
atype = annot['type']
except Exception:
continue
text = ''
if atype == 'bookmark':
text = annot['title']
elif atype == 'highlight':
text = annot.get('highlighted_text') or ''
yield {
'id': rowid,
'book_id': book_id,
'format': fmt,
'user_type': user_type,
'user': user,
'text': text,
'annotation': annot,
}
def all_annotation_users(self):
return self.execute('SELECT DISTINCT user_type, user FROM annotations')

View File

@ -2310,6 +2310,10 @@ class Cache(object):
def all_annotation_types(self):
return tuple(self.backend.all_annotation_types())
@read_api
def all_annotations(self, restrict_to_user=None, limit=None, annotation_type=None):
return tuple(self.backend.all_annotations(restrict_to_user, limit, annotation_type))
@read_api
def search_annotations(
self,

View File

@ -9,7 +9,7 @@ from textwrap import fill
from PyQt5.Qt import (
QApplication, QCheckBox, QComboBox, QCursor, QFont, QHBoxLayout, QIcon, QLabel,
QPalette, QPushButton, QSize, QSplitter, Qt, QTextBrowser, QToolButton,
QPalette, QPushButton, QSize, QSplitter, Qt, QTextBrowser, QTimer, QToolButton,
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget, pyqtSignal
)
@ -43,16 +43,17 @@ class BusyCursor(object):
class AnnotsResultsDelegate(ResultsDelegate):
add_ellipsis = False
emphasize_text = True
def result_data(self, result):
if not isinstance(result, dict):
return None, None, None, None
full_text = result['text'].replace('0x1f', ' ')
parts = full_text.split('\x1d')
full_text = result['text'].replace('\x1f', ' ')
parts = full_text.split('\x1d', 2)
before = after = ''
if len(parts) > 2:
before, text = parts[:2]
after = ' '.join(parts[2:]).replace('\x1d', '')
after = parts[2].replace('\x1d', '')
elif len(parts) == 2:
before, text = parts
else:
@ -82,8 +83,9 @@ class ResultsList(QTreeWidget):
if isinstance(r, dict):
self.open_annotation.emit(r['book_id'], r['format'], r['annotation'])
def set_results(self, results):
def set_results(self, results, emphasize_text):
self.clear()
self.delegate.emphasize_text = emphasize_text
self.number_of_results = 0
self.item_map = []
book_id_map = {}
@ -191,8 +193,12 @@ class Restrictions(QWidget):
tb.clear()
tb.addItem(' ', ' ')
for user_type, user in db.all_annotation_users():
q = '{}: {}'.format(user_type, user)
tb.addItem(q, '{}:{}'.format(user_type, user))
q = display_name = '{}: {}'.format(user_type, user)
if q == 'web: *':
display_name = _('Anonymous content server users')
elif q == 'local: viewer':
display_name = _('Local viewer users')
tb.addItem(display_name, '{}:{}'.format(user_type, user))
if before:
row = tb.findData(before)
if row > -1:
@ -218,7 +224,7 @@ class BrowsePanel(QWidget):
l.addLayout(h)
self.search_box = sb = SearchBox(self)
sb.initialize('library-annotations-browser-search-box')
sb.cleared.connect(self.cleared)
sb.cleared.connect(self.cleared, type=Qt.QueuedConnection)
sb.lineEdit().returnPressed.connect(self.show_next)
sb.lineEdit().setPlaceholderText(_('Enter words to search for'))
h.addWidget(sb)
@ -251,45 +257,46 @@ class BrowsePanel(QWidget):
db = current_db()
self.search_box.setFocus(Qt.OtherFocusReason)
self.restrictions.re_initialize(db)
self.cleared()
self.current_query = None
self.results_list.clear()
def sizeHint(self):
return QSize(450, 600)
@property
def restrict_to_user(self):
user = self.restrictions.user_box.currentData()
if user and ':' in user:
return user.split(':', 1)
@property
def effective_query(self):
text = self.search_box.lineEdit().text().strip()
if not text:
return None
atype = self.restrictions.types_box.currentData()
if not atype or not atype.strip():
atype = None
user = self.restrictions.user_box.currentData()
restrict_to_user = None
if user and ':' in user:
restrict_to_user = user.split(':', 1)
return {
'fts_engine_query': text,
'annotation_type': atype,
'restrict_to_user': restrict_to_user,
'annotation_type': (atype or '').strip(),
'restrict_to_user': self.restrict_to_user,
'use_stemming': bool(self.use_stemmer.isChecked()),
}
def cleared(self):
self.current_query = None
self.results_list.clear()
self.effective_query_changed()
def do_find(self, backwards=False):
q = self.effective_query
if not q:
return
if q == self.current_query:
self.results_list.show_next(backwards)
return
with BusyCursor():
db = current_db()
results = db.search_annotations(highlight_start='\x1d', highlight_end='\x1d', snippet_size=64, **q)
self.results_list.set_results(results)
if not q['fts_engine_query']:
results = db.all_annotations(restrict_to_user=q['restrict_to_user'], limit=4096, annotation_type=q['annotation_type'])
else:
results = db.search_annotations(highlight_start='\x1d', highlight_end='\x1d', snippet_size=64, **q)
self.results_list.set_results(results, bool(q['fts_engine_query']))
self.current_query = q
def effective_query_changed(self):
@ -454,6 +461,8 @@ class AnnotationsBrowser(Dialog):
h.addWidget(us), h.addStretch(10), h.addWidget(self.bb)
def show_dialog(self):
if self.browse_panel.current_query is None:
QTimer.singleShot(80, self.browse_panel.effective_query_changed)
if self.parent() is None:
self.exec_()
else:

View File

@ -17,6 +17,7 @@ from calibre.gui2.widgets2 import HistoryComboBox
class ResultsDelegate(QStyledItemDelegate): # {{{
add_ellipsis = True
emphasize_text = True
def result_data(self, result):
if not hasattr(result, 'is_hidden'):
@ -37,8 +38,11 @@ class ResultsDelegate(QStyledItemDelegate): # {{{
c = p.color(group, c)
painter.setPen(c)
font = option.font
emphasis_font = QFont(font)
emphasis_font.setBold(True)
if self.emphasize_text:
emphasis_font = QFont(font)
emphasis_font.setBold(True)
else:
emphasis_font = font
flags = Qt.AlignTop | Qt.TextSingleLine | Qt.TextIncludeTrailingSpaces
rect = option.rect.adjusted(option.decorationSize.width() + 4 if is_hidden else 0, 0, 0, 0)
painter.setClipRect(rect)