From d915e49815bcfac404e93d69ac6b996cbc6b5710 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 20 Jan 2013 23:27:30 +0530 Subject: [PATCH] Move _match to db.search module --- src/calibre/db/search.py | 45 +++++++++++++++ src/calibre/gui2/library/models.py | 9 +-- .../gui2/store/config/chooser/models.py | 11 ++-- .../gui2/store/stores/mobileread/models.py | 19 ++++--- src/calibre/library/caches.py | 55 +++---------------- 5 files changed, 74 insertions(+), 65 deletions(-) diff --git a/src/calibre/db/search.py b/src/calibre/db/search.py index 5360c9ef38..de95cf69dc 100644 --- a/src/calibre/db/search.py +++ b/src/calibre/db/search.py @@ -13,9 +13,15 @@ from datetime import timedelta from calibre.utils.config_base import prefs from calibre.utils.date import parse_date, UNDEFINED_DATE, now +from calibre.utils.icu import primary_find from calibre.utils.search_query_parser import SearchQueryParser, ParseException # TODO: Thread safety of saved searches +CONTAINS_MATCH = 0 +EQUALS_MATCH = 1 +REGEXP_MATCH = 2 + +# Utils {{{ def force_to_bool(val): if isinstance(val, (str, unicode)): @@ -33,6 +39,45 @@ def force_to_bool(val): val = None return val +def _match(query, value, matchkind, use_primary_find_in_search=True): + if query.startswith('..'): + query = query[1:] + sq = query[1:] + internal_match_ok = True + else: + internal_match_ok = False + for t in value: + try: ### ignore regexp exceptions, required because search-ahead tries before typing is finished + t = icu_lower(t) + if (matchkind == EQUALS_MATCH): + if internal_match_ok: + if query == t: + return True + comps = [c.strip() for c in t.split('.') if c.strip()] + for comp in comps: + if sq == comp: + return True + elif query[0] == '.': + if t.startswith(query[1:]): + ql = len(query) - 1 + if (len(t) == ql) or (t[ql:ql+1] == '.'): + return True + elif query == t: + return True + elif matchkind == REGEXP_MATCH: + if re.search(query, t, re.I|re.UNICODE): + return True + elif matchkind == CONTAINS_MATCH: + if use_primary_find_in_search: + if primary_find(query, t)[0] != -1: + return True + elif query in t: + return True + except re.error: + pass + return False +# }}} + class DateSearch(object): # {{{ def __init__(self): diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 891b775448..bbd8566a37 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -16,12 +16,12 @@ from calibre.utils.pyparsing import ParseException from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors from calibre.ebooks.metadata.book.base import SafeFormat from calibre.ptempfile import PersistentTemporaryFile -from calibre.utils.config import tweaks, device_prefs +from calibre.utils.config import tweaks, device_prefs, prefs from calibre.utils.date import dt_factory, qt_to_dt, as_local_time from calibre.utils.icu import sort_key from calibre.utils.search_query_parser import SearchQueryParser -from calibre.library.caches import (_match, CONTAINS_MATCH, EQUALS_MATCH, - REGEXP_MATCH, MetadataBackup, force_to_bool) +from calibre.db.search import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH +from calibre.library.caches import (MetadataBackup, force_to_bool) from calibre.library.save_to_disk import find_plugboard from calibre import strftime, isbytestring from calibre.constants import filesystem_encoding, DEBUG @@ -1037,6 +1037,7 @@ class OnDeviceSearch(SearchQueryParser): # {{{ } for x in ('author', 'format'): q[x+'s'] = q[x] + upf = prefs['use_primary_find_in_search'] for index, row in enumerate(self.model.db): for locvalue in locations: accessor = q[locvalue] @@ -1063,7 +1064,7 @@ class OnDeviceSearch(SearchQueryParser): # {{{ vals = accessor(row).split(',') else: vals = [accessor(row)] - if _match(query, vals, m): + if _match(query, vals, m, use_primary_find_in_search=upf): matches.add(index) break except ValueError: # Unicode errors diff --git a/src/calibre/gui2/store/config/chooser/models.py b/src/calibre/gui2/store/config/chooser/models.py index 24f6bdfc25..036b45bcaf 100644 --- a/src/calibre/gui2/store/config/chooser/models.py +++ b/src/calibre/gui2/store/config/chooser/models.py @@ -10,8 +10,8 @@ from PyQt4.Qt import (Qt, QAbstractItemModel, QIcon, QVariant, QModelIndex, QSiz from calibre.gui2 import NONE from calibre.customize.ui import is_disabled, disable_plugin, enable_plugin -from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \ - REGEXP_MATCH +from calibre.db.search import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH +from calibre.utils.config_base import prefs from calibre.utils.icu import sort_key from calibre.utils.search_query_parser import SearchQueryParser @@ -60,13 +60,13 @@ class Matches(QAbstractItemModel): index = self.createIndex(i, 0) data = QVariant(True) self.setData(index, data, Qt.CheckStateRole) - + def enable_none(self): for i in xrange(len(self.matches)): index = self.createIndex(i, 0) data = QVariant(False) self.setData(index, data, Qt.CheckStateRole) - + def enable_invert(self): for i in xrange(len(self.matches)): self.toggle_plugin(self.createIndex(i, 0)) @@ -243,6 +243,7 @@ class SearchFilter(SearchQueryParser): 'name': lambda x : x.name.lower(), } q['formats'] = q['format'] + upf = prefs['use_primary_find_in_search'] for sr in self.srs: for locvalue in locations: accessor = q[locvalue] @@ -276,7 +277,7 @@ class SearchFilter(SearchQueryParser): vals = accessor(sr).split(',') else: vals = [accessor(sr)] - if _match(query, vals, m): + if _match(query, vals, m, use_primary_find_in_search=upf): matches.add(sr) break except ValueError: # Unicode errors diff --git a/src/calibre/gui2/store/stores/mobileread/models.py b/src/calibre/gui2/store/stores/mobileread/models.py index 297707e248..60f038c4e2 100644 --- a/src/calibre/gui2/store/stores/mobileread/models.py +++ b/src/calibre/gui2/store/stores/mobileread/models.py @@ -11,13 +11,13 @@ from operator import attrgetter from PyQt4.Qt import (Qt, QAbstractItemModel, QModelIndex, QVariant, pyqtSignal) from calibre.gui2 import NONE -from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \ - REGEXP_MATCH +from calibre.db.search import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH +from calibre.utils.config_base import prefs from calibre.utils.icu import sort_key from calibre.utils.search_query_parser import SearchQueryParser class BooksModel(QAbstractItemModel): - + total_changed = pyqtSignal(int) HEADERS = [_('Title'), _('Author(s)'), _('Format')] @@ -37,8 +37,8 @@ class BooksModel(QAbstractItemModel): return self.books[row] else: return None - - def search(self, filter): + + def search(self, filter): self.filter = filter.strip() if not self.filter: self.books = self.all_books @@ -50,7 +50,7 @@ class BooksModel(QAbstractItemModel): self.layoutChanged.emit() self.sort(self.sort_col, self.sort_order) self.total_changed.emit(self.rowCount()) - + def index(self, row, column, parent=QModelIndex()): return self.createIndex(row, column) @@ -64,7 +64,7 @@ class BooksModel(QAbstractItemModel): def columnCount(self, *args): return len(self.HEADERS) - + def headerData(self, section, orientation, role): if role != Qt.DisplayRole: return NONE @@ -112,7 +112,7 @@ class BooksModel(QAbstractItemModel): class SearchFilter(SearchQueryParser): - + USABLE_LOCATIONS = [ 'all', 'author', @@ -161,6 +161,7 @@ class SearchFilter(SearchQueryParser): } for x in ('author', 'format'): q[x+'s'] = q[x] + upf = prefs['use_primary_find_in_search'] for sr in self.srs: for locvalue in locations: accessor = q[locvalue] @@ -182,7 +183,7 @@ class SearchFilter(SearchQueryParser): m = matchkind vals = [accessor(sr)] - if _match(query, vals, m): + if _match(query, vals, m, use_primary_find_in_search=upf): matches.add(sr) break except ValueError: # Unicode errors diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 507305528d..b453c654df 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import re, itertools, time, traceback, locale +import itertools, time, traceback, locale from itertools import repeat, izip, imap from datetime import timedelta from threading import Thread @@ -16,10 +16,10 @@ from calibre.utils.date import parse_date, now, UNDEFINED_DATE, clean_date_for_s from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.pyparsing import ParseException from calibre.utils.localization import (canonicalize_lang, lang_map, get_udc) +from calibre.db.search import CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH, _match from calibre.ebooks.metadata import title_sort, author_to_author_sort from calibre.ebooks.metadata.opf2 import metadata_to_opf from calibre import prints -from calibre.utils.icu import primary_find class MetadataBackup(Thread): # {{{ ''' @@ -118,7 +118,6 @@ class MetadataBackup(Thread): # {{{ # }}} - ### Global utility function for get_match here and in gui2/library.py # This is a global for performance pref_use_primary_find_in_search = False @@ -127,47 +126,6 @@ def set_use_primary_find_in_search(toWhat): global pref_use_primary_find_in_search pref_use_primary_find_in_search = toWhat -CONTAINS_MATCH = 0 -EQUALS_MATCH = 1 -REGEXP_MATCH = 2 -def _match(query, value, matchkind): - if query.startswith('..'): - query = query[1:] - sq = query[1:] - internal_match_ok = True - else: - internal_match_ok = False - for t in value: - try: ### ignore regexp exceptions, required because search-ahead tries before typing is finished - t = icu_lower(t) - if (matchkind == EQUALS_MATCH): - if internal_match_ok: - if query == t: - return True - comps = [c.strip() for c in t.split('.') if c.strip()] - for comp in comps: - if sq == comp: - return True - elif query[0] == '.': - if t.startswith(query[1:]): - ql = len(query) - 1 - if (len(t) == ql) or (t[ql:ql+1] == '.'): - return True - elif query == t: - return True - elif matchkind == REGEXP_MATCH: - if re.search(query, t, re.I|re.UNICODE): - return True - elif matchkind == CONTAINS_MATCH: - if pref_use_primary_find_in_search: - if primary_find(query, t)[0] != -1: - return True - elif query in t: - return True - except re.error: - pass - return False - def force_to_bool(val): if isinstance(val, (str, unicode)): try: @@ -576,7 +534,8 @@ class ResultCache(SearchQueryParser): # {{{ continue k = parts[:1] v = parts[1:] - if keyq and not _match(keyq, k, keyq_mkind): + if keyq and not _match(keyq, k, keyq_mkind, + use_primary_find_in_search=pref_use_primary_find_in_search): continue if valq: if valq == 'true': @@ -586,7 +545,8 @@ class ResultCache(SearchQueryParser): # {{{ if v: add_if_nothing_matches = False continue - elif not _match(valq, v, valq_mkind): + elif not _match(valq, v, valq_mkind, + use_primary_find_in_search=pref_use_primary_find_in_search): continue matches.add(id_) @@ -851,7 +811,8 @@ class ResultCache(SearchQueryParser): # {{{ vals = [v.strip() for v in item[loc].split(is_multiple_cols[loc])] else: vals = [item[loc]] ### make into list to make _match happy - if _match(q, vals, matchkind): + if _match(q, vals, matchkind, + use_primary_find_in_search=pref_use_primary_find_in_search): matches.add(item[0]) continue current_candidates -= matches