on master help sign in
Merge remote-tracking branch 'upstream/master'
829c758dalf
searx/engines/__init__.py+16−4
@@ -142,16 +142,28 @@ def get_engines_stats():
142 })
142 })
143
143
144 for engine in pageloads:
144 for engine in pageloads:
145+ if max_pageload:
145− engine['percentage'] = int(engine['avg'] / max_pageload * 100)
146+ engine['percentage'] = int(engine['avg'] / max_pageload * 100)
147+ else:
148+ engine['percentage'] = 0
146
149
147 for engine in results:
150 for engine in results:
151+ if max_results:
148− engine['percentage'] = int(engine['avg'] / max_results * 100)
152+ engine['percentage'] = int(engine['avg'] / max_results * 100)
153+ else:
154+ engine['percentage'] = 0
149
155
150 for engine in scores:
156 for engine in scores:
157+ if max_score:
151− engine['percentage'] = int(engine['avg'] / max_score * 100)
158+ engine['percentage'] = int(engine['avg'] / max_score * 100)
159+ else:
160+ engine['percentage'] = 0
152
161
153 for engine in scores_per_result:
162 for engine in scores_per_result:
163+ if max_score_per_result:
154− engine['percentage'] = int(engine['avg'] / max_score_per_result * 100)
164+ engine['percentage'] = int(engine['avg'] / max_score_per_result * 100)
165+ else:
166+ engine['percentage'] = 0
155
167
156 for engine in errors:
168 for engine in errors:
157 if max_errors:
169 if max_errors:
searx/query.pynew file+127
@@ -0,0 +1,127 @@
1+#!/usr/bin/env python
2+
3+'''
4+searx is free software: you can redistribute it and/or modify
5+it under the terms of the GNU Affero General Public License as published by
6+the Free Software Foundation, either version 3 of the License, or
7+(at your option) any later version.
8+
9+searx is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU Affero General Public License for more details.
13+
14+You should have received a copy of the GNU Affero General Public License
15+along with searx. If not, see < http://www.gnu.org/licenses/ >.
16+
17+(C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
18+'''
19+
20+from searx.languages import language_codes
21+from searx.engines import (
22+ categories, engines, engine_shortcuts
23+)
24+import string
25+import re
26+
27+
28+class Query(object):
29+ """parse query"""
30+
31+ def __init__(self, query, blocked_engines):
32+ self.query = query
33+ self.blocked_engines = []
34+
35+ if blocked_engines:
36+ self.blocked_engines = blocked_engines
37+
38+ self.query_parts = []
39+ self.engines = []
40+ self.languages = []
41+
42+ # parse query, if tags are set, which change the serch engine or search-language
43+ def parse_query(self):
44+ self.query_parts = []
45+
46+ # split query, including whitespaces
47+ raw_query_parts = re.split(r'(\s+)', self.query)
48+
49+ parse_next = True
50+
51+ for query_part in raw_query_parts:
52+ if not parse_next:
53+ self.query_parts[-1] += query_part
54+ continue
55+
56+ parse_next = False
57+
58+ # part does only contain spaces, skip
59+ if query_part.isspace()\
60+ or query_part == '':
61+ parse_next = True
62+ self.query_parts.append(query_part)
63+ continue
64+
65+ # this force a language
66+ if query_part[0] == ':':
67+ lang = query_part[1:].lower()
68+
69+ # check if any language-code is equal with declared language-codes
70+ for lc in language_codes:
71+ lang_id, lang_name, country = map(str.lower, lc)
72+
73+ # if correct language-code is found, set it as new search-language
74+ if lang == lang_id\
75+ or lang_id.startswith(lang)\
76+ or lang == lang_name\
77+ or lang == country:
78+ parse_next = True
79+ self.languages.append(lang)
80+ break
81+
82+ # this force a engine or category
83+ if query_part[0] == '!':
84+ prefix = query_part[1:].replace('_', ' ')
85+
86+ # check if prefix is equal with engine shortcut
87+ if prefix in engine_shortcuts\
88+ and not engine_shortcuts[prefix] in self.blocked_engines:
89+ parse_next = True
90+ self.engines.append({'category': 'none',
91+ 'name': engine_shortcuts[prefix]})
92+
93+ # check if prefix is equal with engine name
94+ elif prefix in engines\
95+ and not prefix in self.blocked_engines:
96+ parse_next = True
97+ self.engines.append({'category': 'none',
98+ 'name': prefix})
99+
100+ # check if prefix is equal with categorie name
101+ elif prefix in categories:
102+ # using all engines for that search, which are declared under that categorie name
103+ parse_next = True
104+ self.engines.extend({'category': prefix,
105+ 'name': engine.name}
106+ for engine in categories[prefix]
107+ if not engine in self.blocked_engines)
108+
109+ # append query part to query_part list
110+ self.query_parts.append(query_part)
111+
112+ def changeSearchQuery(self, search_query):
113+ if len(self.query_parts):
114+ self.query_parts[-1] = search_query
115+ else:
116+ self.query_parts.append(search_query)
117+
118+ def getSearchQuery(self):
119+ if len(self.query_parts):
120+ return self.query_parts[-1]
121+ else:
122+ return ''
123+
124+ def getFullQuery(self):
125+ # get full querry including whitespaces
126+ return string.join(self.query_parts, '')
127+
searx/search.py+10−55
@@ -26,6 +26,7 @@ from searx.engines import (
26)
26)
27from searx.languages import language_codes
27from searx.languages import language_codes
28from searx.utils import gen_useragent
28from searx.utils import gen_useragent
29+from searx.query import Query
29
30
30
31
31number_of_searches = 0
32number_of_searches = 0
@@ -321,7 +322,15 @@ class Search(object):
321 self.pageno = int(pageno_param)
322 self.pageno = int(pageno_param)
322
323
323 # parse query, if tags are set, which change the serch engine or search-language
324 # parse query, if tags are set, which change the serch engine or search-language
325+ query_obj = Query(self.query, self.blocked_engines)
324− self.parse_query()
326+ query_obj.parse_query()
327+
328+ # get last selected language in query, if possible
329+ # TODO support search with multible languages
330+ if len(query_obj.languages):
331+ self.lang = query_obj.languages[-1]
332+
333+ self.engines = query_obj.engines
325
334
326 self.categories = []
335 self.categories = []
327
336
@@ -362,60 +371,6 @@ class Search(object):
362 for x in categories[categ]
371 for x in categories[categ]
363 if not x.name in self.blocked_engines)
372 if not x.name in self.blocked_engines)
364
373
365− # parse query, if tags are set, which change the serch engine or search-language
366− def parse_query(self):
367− query_parts = self.query.split()
368− modified = False
369−
370− # check if language-prefix is set
371− if query_parts[0].startswith(':'):
372− lang = query_parts[0][1:].lower()
373−
374− # check if any language-code is equal with declared language-codes
375− for lc in language_codes:
376− lang_id, lang_name, country = map(str.lower, lc)
377−
378− # if correct language-code is found, set it as new search-language
379− if lang == lang_id\
380− or lang_id.startswith(lang)\
381− or lang == lang_name\
382− or lang == country:
383− self.lang = lang
384− modified = True
385− break
386−
387− # check if category/engine prefix is set
388− elif query_parts[0].startswith('!'):
389− prefix = query_parts[0][1:].replace('_', ' ')
390−
391− # check if prefix is equal with engine shortcut
392− if prefix in engine_shortcuts\
393− and not engine_shortcuts[prefix] in self.blocked_engines:
394− modified = True
395− self.engines.append({'category': 'none',
396− 'name': engine_shortcuts[prefix]})
397−
398− # check if prefix is equal with engine name
399− elif prefix in engines\
400− and not prefix in self.blocked_engines:
401− modified = True
402− self.engines.append({'category': 'none',
403− 'name': prefix})
404−
405− # check if prefix is equal with categorie name
406− elif prefix in categories:
407− modified = True
408− # using all engines for that search, which are declared under that categorie name
409− self.engines.extend({'category': prefix,
410− 'name': engine.name}
411− for engine in categories[prefix]
412− if not engine in self.blocked_engines)
413−
414− # if language, category or engine were specificed in this query, search for more tags which does the same
415− if modified:
416− self.query = self.query.replace(query_parts[0], '', 1).strip()
417− self.parse_query()
418−
419 # do search-request
374 # do search-request
420 def search(self, request):
375 def search(self, request):
421 global number_of_searches
376 global number_of_searches
searx/translations/fr/LC_MESSAGES/messages.po+2−2
@@ -23,11 +23,11 @@ msgstr ""
23
23
24#: searx/webapp.py:167
24#: searx/webapp.py:167
25msgid "{minutes} minute(s) ago"
25msgid "{minutes} minute(s) ago"
26−msgstr ""
26+msgstr "Il y a {minutes} minute(s)"
27
27
28#: searx/webapp.py:169
28#: searx/webapp.py:169
29msgid "{hours} hour(s), {minutes} minute(s) ago"
29msgid "{hours} hour(s), {minutes} minute(s) ago"
30−msgstr ""
30+msgstr "Il y a {hours} heure(s), {minutes} minute(s)"
31
31
32#: searx/engines/__init__.py:311
32#: searx/engines/__init__.py:311
33msgid "Page loads (sec)"
33msgid "Page loads (sec)"
searx/webapp.py+27−3
@@ -47,6 +47,7 @@ from searx.utils import (
47from searx.https_rewrite import https_rules
47from searx.https_rewrite import https_rules
48from searx.languages import language_codes
48from searx.languages import language_codes
49from searx.search import Search
49from searx.search import Search
50+from searx.query import Query
50from searx.autocomplete import backends as autocomplete_backends
51from searx.autocomplete import backends as autocomplete_backends
51
52
52
53
@@ -310,23 +311,46 @@ def autocompleter():
310 """Return autocompleter results"""
311 """Return autocompleter results"""
311 request_data = {}
312 request_data = {}
312
313
314+ # select request method
313 if request.method == 'POST':
315 if request.method == 'POST':
314 request_data = request.form
316 request_data = request.form
315 else:
317 else:
316 request_data = request.args
318 request_data = request.args
317
319
320+ # set blocked engines
321+ if request.cookies.get('blocked_engines'):
322+ blocked_engines = request.cookies['blocked_engines'].split(',') # noqa
323+ else:
324+ blocked_engines = []
325+
326+ # parse query
318− query = request_data.get('q', '').encode('utf-8')
327+ query = Query(request_data.get('q', '').encode('utf-8'), blocked_engines)
328+ query.parse_query()
319
329
330+ # check if search query is set
320− if not query:
331+ if not query.getSearchQuery():
321 return
332 return
322
333
334+ # run autocompleter
323 completer = autocomplete_backends.get(request.cookies.get('autocomplete'))
335 completer = autocomplete_backends.get(request.cookies.get('autocomplete'))
324
336
337+ # check if valid autocompleter is selected
325 if not completer:
338 if not completer:
326 return
339 return
327
340
341+ # run autocompletion
328− results = completer(query)
342+ raw_results = completer(query.getSearchQuery())
343+
344+ # parse results (write :language and !engine back to result string)
345+ results = []
346+ for result in raw_results:
347+ result_query = query
348+ result_query.changeSearchQuery(result)
349+
350+ # add parsed result
351+ results.append(result_query.getFullQuery())
329
352
353+ # return autocompleter results
330 if request_data.get('format') == 'x-suggestions':
354 if request_data.get('format') == 'x-suggestions':
331 return Response(json.dumps([query, results]),
355 return Response(json.dumps([query, results]),
332 mimetype='application/json')
356 mimetype='application/json')