From 3bbc1f1342b348163e68430c8e4ccb06757f5bbc Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 10 Jun 2012 11:44:14 +0200
Subject: [PATCH] Permit use of arbitrary category fields and grouped search
terms in "similar searches". Permit changing the match kind.
---
src/calibre/gui2/actions/similar_books.py | 64 ++++++++++++++---------
src/calibre/gui2/preferences/search.py | 9 +++-
src/calibre/gui2/preferences/search.ui | 27 ++++++++--
src/calibre/library/database2.py | 4 ++
4 files changed, 74 insertions(+), 30 deletions(-)
diff --git a/src/calibre/gui2/actions/similar_books.py b/src/calibre/gui2/actions/similar_books.py
index 6967e70db5..8c7fe1d0ed 100644
--- a/src/calibre/gui2/actions/similar_books.py
+++ b/src/calibre/gui2/actions/similar_books.py
@@ -26,40 +26,56 @@ class SimilarBooksAction(InterfaceAction):
(_('Books in this series'), 'books_in_series.png', 'series',
_('Alt+Shift+S')),
(_('Books by this publisher'), 'publisher.png', 'publisher', _('Alt+P')),
- (_('Books with the same tags'), 'tags.png', 'tag', _('Alt+T')),]:
+ (_('Books with the same tags'), 'tags.png', 'tags', _('Alt+T')),]:
ac = self.create_action(spec=(text, icon, None, shortcut),
attr=target)
m.addAction(ac)
ac.triggered.connect(partial(self.show_similar_books, target))
self.qaction.setMenu(m)
- def show_similar_books(self, type, *args):
- search, join = [], ' '
+ def show_similar_books(self, typ, *args):
idx = self.gui.library_view.currentIndex()
- db = self.gui.library_view.model().db
if not idx.isValid():
return
+ db = idx.model().db
row = idx.row()
- if type == 'series':
- series = idx.model().db.series(row)
- if series:
- search = [db.prefs['similar_series_search_key'] + ':"'+series+'"']
- elif type == 'publisher':
- publisher = idx.model().db.publisher(row)
- if publisher:
- search = [db.prefs['similar_publisher_search_key'] + ':"'+publisher+'"']
- elif type == 'tag':
- tags = idx.model().db.tags(row)
- if tags:
- search = [db.prefs['similar_tags_search_key'] + ':"='+t+'"'
- for t in tags.split(',')]
- elif type in ('author', 'authors'):
- authors = idx.model().db.authors(row)
- if authors:
- search = [db.prefs['similar_authors_search_key'] +
- ':"='+a.strip().replace('|', ',')+'"' \
- for a in authors.split(',')]
- join = ' or '
+
+ # Get the parameters for this search
+ col = db.prefs['similar_' + typ + '_search_key']
+ match = db.prefs['similar_' + typ + '_match_kind']
+ if match == _('Match all'):
+ join = ' and '
+ else:
+ join = ' or '
+
+ # Get all the data for the current record
+ mi = db.get_metadata(row)
+
+ # Get the definitive field name to use for this search. If the field
+ # is a grouped search term, the function returns the list of fields that
+ # are to be searched, otherwise it returns the field name.
+ loc = db.field_metadata.search_term_to_field_key(icu_lower(col))
+ if isinstance(loc, list):
+ # Grouped search terms are a list of fields. Get all the values,
+ # pruning duplicates
+ val = set()
+ for f in loc:
+ v = mi.get(f, None)
+ if not v:
+ continue
+ if isinstance(v, list):
+ val.update(v)
+ else:
+ val.add(v)
+ else:
+ # Get the value of the requested field. Can be a list or a simple val
+ val = mi.get(col, None)
+ if not val:
+ return
+
+ if not isinstance(val, (list, set)):
+ val = [val]
+ search = [col + ':"='+t+'"' for t in val]
if search:
self.gui.search.set_search_string(join.join(search),
store_in_history=True)
diff --git a/src/calibre/gui2/preferences/search.py b/src/calibre/gui2/preferences/search.py
index 336e30e4ba..ea0cae3ab2 100644
--- a/src/calibre/gui2/preferences/search.py
+++ b/src/calibre/gui2/preferences/search.py
@@ -71,6 +71,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.gst_value.update_items_cache(fl)
self.fill_gst_box(select=None)
+ self.category_fields = fl
+ ml = [_('Match any'), _('Match all')]
+ r('similar_authors_match_kind', db.prefs, choices=ml)
+ r('similar_tags_match_kind', db.prefs, choices=ml)
+ r('similar_series_match_kind', db.prefs, choices=ml)
+ r('similar_publisher_match_kind', db.prefs, choices=ml)
self.set_similar_fields(initial=True)
self.similar_authors_search_key.currentIndexChanged[int].connect(self.something_changed)
self.similar_tags_search_key.currentIndexChanged[int].connect(self.something_changed)
@@ -107,7 +113,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
val = self.db.prefs[name]
field.blockSignals(True)
field.clear()
- choices = [first_item]
+ choices = []
+ choices.extend(self.category_fields)
choices.extend(sorted(self.gst.keys(), key=sort_key))
field.addItems(choices)
dex = field.findText(val)
diff --git a/src/calibre/gui2/preferences/search.ui b/src/calibre/gui2/preferences/search.ui
index afe212d661..f41a42e864 100644
--- a/src/calibre/gui2/preferences/search.ui
+++ b/src/calibre/gui2/preferences/search.ui
@@ -207,13 +207,14 @@ to be shown as user categories
What to search when searching similar books
- -
+
-
- <p>When you search for similar books by right clicking the book and selecting "Similar books...",
+ <p>When you search for similar books by right clicking the
+ book and selecting "Similar books...",
calibre constructs a search using the column lookup names defined below.
By changing the lookup name to a grouped search term you can
- search multiple columns.</p>
+ search multiple columns at once.</p>
true
@@ -238,13 +239,17 @@ to be shown as user categories
-
+
+
+
+ -
Similar series:
- -
+
-
@@ -254,6 +259,10 @@ to be shown as user categories
+ -
+
+
+
-
@@ -266,16 +275,24 @@ to be shown as user categories
-
+
+
+
+ -
Similar publishers:
- -
+
-
+ -
+
+
+
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index dd2a265dba..d3f27e36d4 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -237,9 +237,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
defs['column_color_rules'] = []
defs['grouped_search_make_user_categories'] = []
defs['similar_authors_search_key'] = 'author'
+ defs['similar_authors_match_kind'] = _('Match any')
defs['similar_publisher_search_key'] = 'publisher'
+ defs['similar_publisher_match_kind'] = _('Match any')
defs['similar_tags_search_key'] = 'tags'
+ defs['similar_tags_match_kind'] = _('Match all')
defs['similar_series_search_key'] = 'series'
+ defs['similar_series_match_kind'] = _('Match any')
# Migrate the bool tristate tweak
defs['bools_are_tristate'] = \