mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Store: Search allows for main window location and boolean filtering.
This commit is contained in:
parent
66beba7708
commit
b6f44d0b7c
@ -8,6 +8,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from random import shuffle
|
from random import shuffle
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -20,9 +21,12 @@ from calibre import browser
|
|||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||||
from calibre.gui2.store.search_ui import Ui_Dialog
|
from calibre.gui2.store.search_ui import Ui_Dialog
|
||||||
|
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
|
||||||
|
REGEXP_MATCH
|
||||||
from calibre.utils.config import DynamicConfig
|
from calibre.utils.config import DynamicConfig
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.magick.draw import thumbnail
|
from calibre.utils.magick.draw import thumbnail
|
||||||
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
|
|
||||||
HANG_TIME = 75000 # milliseconds seconds
|
HANG_TIME = 75000 # milliseconds seconds
|
||||||
TIMEOUT = 75 # seconds
|
TIMEOUT = 75 # seconds
|
||||||
@ -290,10 +294,14 @@ class SearchThread(Thread):
|
|||||||
while self._run and not self.tasks.empty():
|
while self._run and not self.tasks.empty():
|
||||||
try:
|
try:
|
||||||
query, store_name, store_plugin, timeout = self.tasks.get()
|
query, store_name, store_plugin, timeout = self.tasks.get()
|
||||||
for res in store_plugin.search(query, timeout=timeout):
|
squery = query
|
||||||
|
for loc in SearchFilter.USABLE_LOCATIONS:
|
||||||
|
squery = re.sub(r'%s:"?(?P<a>[^\s"]+)"?' % loc, '\g<a>', squery)
|
||||||
|
for res in store_plugin.search(squery, timeout=timeout):
|
||||||
if not self._run:
|
if not self._run:
|
||||||
return
|
return
|
||||||
res.store_name = store_name
|
res.store_name = store_name
|
||||||
|
if SearchFilter(res).parse(query):
|
||||||
self.results.put(res)
|
self.results.put(res)
|
||||||
self.tasks.task_done()
|
self.tasks.task_done()
|
||||||
except:
|
except:
|
||||||
@ -450,3 +458,82 @@ class Matches(QAbstractItemModel):
|
|||||||
if reset:
|
if reset:
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
|
||||||
|
class SearchFilter(SearchQueryParser):
|
||||||
|
|
||||||
|
USABLE_LOCATIONS = [
|
||||||
|
'all',
|
||||||
|
'author',
|
||||||
|
'authors',
|
||||||
|
'cover',
|
||||||
|
'price',
|
||||||
|
'title',
|
||||||
|
'store',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, search_result):
|
||||||
|
SearchQueryParser.__init__(self, locations=self.USABLE_LOCATIONS)
|
||||||
|
self.search_result = search_result
|
||||||
|
|
||||||
|
def universal_set(self):
|
||||||
|
return set([self.search_result])
|
||||||
|
|
||||||
|
def get_matches(self, location, query):
|
||||||
|
location = location.lower().strip()
|
||||||
|
if location == 'authors':
|
||||||
|
location = 'author'
|
||||||
|
|
||||||
|
matchkind = CONTAINS_MATCH
|
||||||
|
if len(query) > 1:
|
||||||
|
if query.startswith('\\'):
|
||||||
|
query = query[1:]
|
||||||
|
elif query.startswith('='):
|
||||||
|
matchkind = EQUALS_MATCH
|
||||||
|
query = query[1:]
|
||||||
|
elif query.startswith('~'):
|
||||||
|
matchkind = REGEXP_MATCH
|
||||||
|
query = query[1:]
|
||||||
|
if matchkind != REGEXP_MATCH: ### leave case in regexps because it can be significant e.g. \S \W \D
|
||||||
|
query = query.lower()
|
||||||
|
|
||||||
|
if location not in self.USABLE_LOCATIONS:
|
||||||
|
return set([])
|
||||||
|
matches = set([])
|
||||||
|
all_locs = set(self.USABLE_LOCATIONS) - set(['all'])
|
||||||
|
locations = all_locs if location == 'all' else [location]
|
||||||
|
q = {
|
||||||
|
'author': self.search_result.author.lower(),
|
||||||
|
'cover': self.search_result.cover_url,
|
||||||
|
'format': '',
|
||||||
|
'price': self.search_result.price,
|
||||||
|
'store': self.search_result.store_name.lower(),
|
||||||
|
'title': self.search_result.title.lower(),
|
||||||
|
}
|
||||||
|
for x in ('author', 'format'):
|
||||||
|
q[x+'s'] = q[x]
|
||||||
|
for locvalue in locations:
|
||||||
|
ac_val = q[locvalue]
|
||||||
|
if query == 'true':
|
||||||
|
if ac_val is not None:
|
||||||
|
matches.add(self.search_result)
|
||||||
|
continue
|
||||||
|
if query == 'false':
|
||||||
|
if ac_val is None:
|
||||||
|
matches.add(self.search_result)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
### Can't separate authors because comma is used for name sep and author sep
|
||||||
|
### Exact match might not get what you want. For that reason, turn author
|
||||||
|
### exactmatch searches into contains searches.
|
||||||
|
if locvalue == 'author' and matchkind == EQUALS_MATCH:
|
||||||
|
m = CONTAINS_MATCH
|
||||||
|
else:
|
||||||
|
m = matchkind
|
||||||
|
|
||||||
|
vals = [ac_val]
|
||||||
|
if _match(query, vals, m):
|
||||||
|
matches.add(self.search_result)
|
||||||
|
break
|
||||||
|
except ValueError: # Unicode errors
|
||||||
|
traceback.print_exc()
|
||||||
|
return matches
|
||||||
|
Loading…
x
Reference in New Issue
Block a user