mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-03 19:17:02 -05: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