on master help sign in
[enh] autocompleter server side part
41dd4d9Adam Tauber
searx/settings.yml−3
@@ -5,9 +5,6 @@ server:
5 request_timeout : 2.0 # seconds
5 request_timeout : 2.0 # seconds
6 base_url : False
6 base_url : False
7
7
8−client:
9− autocompleter : False # only for developers, no real results yet
10−
11engines:
8engines:
12 - name : wikipedia
9 - name : wikipedia
13 engine : wikipedia
10 engine : wikipedia
searx/static/js/searx.js+1−1
@@ -9,7 +9,7 @@ if(searx.autocompleter) {
9 timeout: 5 // Correct option?
9 timeout: 5 // Correct option?
10 },
10 },
11 'minLength': 4,
11 'minLength': 4,
12− 'selectMode': 'type-ahead',
12+ // 'selectMode': 'type-ahead',
13 cache: true,
13 cache: true,
14 delay: 300
14 delay: 300
15 });
15 });
searx/templates/base.html+2−2
@@ -15,14 +15,14 @@
15 {% endblock %}
15 {% endblock %}
16 <script type="text/javascript">
16 <script type="text/javascript">
17 searx = {};
17 searx = {};
18− searx.autocompleter = {% if client.autocompleter %}true{% else %}false{% endif %};
18+ searx.autocompleter = {% if autocomplete %}true{% else %}false{% endif %};
19 </script>
19 </script>
20</head>
20</head>
21<body>
21<body>
22<div id="container">
22<div id="container">
23{% block content %}
23{% block content %}
24{% endblock %}
24{% endblock %}
25−{% if client.autocompleter %}
25+{% if autocomplete %}
26<script src="{{ url_for('static', filename='js/mootools-core-1.4.5-min.js') }}" ></script>
26<script src="{{ url_for('static', filename='js/mootools-core-1.4.5-min.js') }}" ></script>
27<script src="{{ url_for('static', filename='js/mootools-autocompleter-1.1.2-min.js') }}" ></script>
27<script src="{{ url_for('static', filename='js/mootools-autocompleter-1.1.2-min.js') }}" ></script>
28{% endif %}
28{% endif %}
searx/templates/preferences.html+13−2
@@ -17,7 +17,7 @@
17 <select name='language'>
17 <select name='language'>
18 <option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Automatic') }}</option>
18 <option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Automatic') }}</option>
19 {% for lang_id,lang_name,country_name in language_codes %}
19 {% for lang_id,lang_name,country_name in language_codes %}
20− <option value={{ lang_id }} {% if lang_id == current_language %}selected="selected"{% endif %}>{{ lang_name}} ({{ country_name }}) - {{ lang_id }}</option>
20+ <option value="{{ lang_id }}" {% if lang_id == current_language %}selected="selected"{% endif %}>{{ lang_name }} ({{ country_name }}) - {{ lang_id }}</option>
21 {% endfor %}
21 {% endfor %}
22 </select>
22 </select>
23 </p>
23 </p>
@@ -27,7 +27,18 @@
27 <p>
27 <p>
28 <select name='locale'>
28 <select name='locale'>
29 {% for locale_id,locale_name in locales.items() %}
29 {% for locale_id,locale_name in locales.items() %}
30− <option value={{ locale_id }} {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name}}</option>
30+ <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
31+ {% endfor %}
32+ </select>
33+ </p>
34+ </fieldset>
35+ <fieldset>
36+ <legend>{{ _('Autocomplete') }}</legend>
37+ <p>
38+ <select name="autocomplete">
39+ <option value=""> - </option>
40+ {% for backend in autocomplete_backends %}
41+ <option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
31 {% endfor %}
42 {% endfor %}
32 </select>
43 </select>
33 </p>
44 </p>
searx/webapp.py+53−20
@@ -41,6 +41,7 @@ from searx.engines import (
41from searx.utils import UnicodeWriter, highlight_content, html_to_text
41from searx.utils import UnicodeWriter, highlight_content, html_to_text
42from searx.languages import language_codes
42from searx.languages import language_codes
43from searx.search import Search
43from searx.search import Search
44+from searx.autocomplete import backends as autocomplete_backends
44
45
45
46
46app = Flask(
47app = Flask(
@@ -91,16 +92,25 @@ def get_base_url():
91
92
92def render(template_name, **kwargs):
93def render(template_name, **kwargs):
93 blocked_engines = request.cookies.get('blocked_engines', '').split(',')
94 blocked_engines = request.cookies.get('blocked_engines', '').split(',')
95+
96+ autocomplete = request.cookies.get('autocomplete')
97+
98+ if autocomplete not in autocomplete_backends:
99+ autocomplete = None
100+
94 nonblocked_categories = (engines[e].categories
101 nonblocked_categories = (engines[e].categories
95 for e in engines
102 for e in engines
96 if e not in blocked_engines)
103 if e not in blocked_engines)
104+
97 nonblocked_categories = set(chain.from_iterable(nonblocked_categories))
105 nonblocked_categories = set(chain.from_iterable(nonblocked_categories))
106+
98 if not 'categories' in kwargs:
107 if not 'categories' in kwargs:
99 kwargs['categories'] = ['general']
108 kwargs['categories'] = ['general']
100 kwargs['categories'].extend(x for x in
109 kwargs['categories'].extend(x for x in
101 sorted(categories.keys())
110 sorted(categories.keys())
102 if x != 'general'
111 if x != 'general'
103 and x in nonblocked_categories)
112 and x in nonblocked_categories)
113+
104 if not 'selected_categories' in kwargs:
114 if not 'selected_categories' in kwargs:
105 kwargs['selected_categories'] = []
115 kwargs['selected_categories'] = []
106 cookie_categories = request.cookies.get('categories', '').split(',')
116 cookie_categories = request.cookies.get('categories', '').split(',')
@@ -109,6 +119,10 @@ def render(template_name, **kwargs):
109 kwargs['selected_categories'].append(ccateg)
119 kwargs['selected_categories'].append(ccateg)
110 if not kwargs['selected_categories']:
120 if not kwargs['selected_categories']:
111 kwargs['selected_categories'] = ['general']
121 kwargs['selected_categories'] = ['general']
122+
123+ if not 'autocomplete' in kwargs:
124+ kwargs['autocomplete'] = autocomplete
125+
112 return render_template(template_name, **kwargs)
126 return render_template(template_name, **kwargs)
113
127
114
128
@@ -122,7 +136,6 @@ def index():
122 if not request.args and not request.form:
136 if not request.args and not request.form:
123 return render(
137 return render(
124 'index.html',
138 'index.html',
125− client=settings.get('client', None)
126 )
139 )
127
140
128 try:
141 try:
@@ -130,7 +143,6 @@ def index():
130 except:
143 except:
131 return render(
144 return render(
132 'index.html',
145 'index.html',
133− client=settings.get('client', None)
134 )
146 )
135
147
136 # TODO moar refactor - do_search integration into Search class
148 # TODO moar refactor - do_search integration into Search class
@@ -212,7 +224,6 @@ def index():
212 return render(
224 return render(
213 'results.html',
225 'results.html',
214 results=search.results,
226 results=search.results,
215− client=settings.get('client', None),
216 q=search.request_data['q'],
227 q=search.request_data['q'],
217 selected_categories=search.categories,
228 selected_categories=search.categories,
218 paging=search.paging,
229 paging=search.paging,
@@ -227,7 +238,6 @@ def about():
227 """Render about page"""
238 """Render about page"""
228 return render(
239 return render(
229 'about.html',
240 'about.html',
230− client=settings.get('client', None)
231 )
241 )
232
242
233
243
@@ -235,27 +245,35 @@ def about():
235def autocompleter():
245def autocompleter():
236 """Return autocompleter results"""
246 """Return autocompleter results"""
237 request_data = {}
247 request_data = {}
238−
248+
239 if request.method == 'POST':
249 if request.method == 'POST':
240 request_data = request.form
250 request_data = request.form
241 else:
251 else:
242 request_data = request.args
252 request_data = request.args
243−
253+
244 # TODO fix XSS-vulnerability
254 # TODO fix XSS-vulnerability
245− autocompleter.querry = request_data.get('q')
255+ query = request_data.get('q')
246− autocompleter.results = []
256+
247−
257+ if not query:
248− if settings['client']['autocompleter']:
258+ return
249− #TODO remove test code and add real autocompletion
259+
250− if autocompleter.querry:
260+ completer = autocomplete_backends.get(request.cookies.get('autocomplete'))
251− autocompleter.results = [autocompleter.querry + " result-1",autocompleter.querry + " result-2",autocompleter.querry + " result-3",autocompleter.querry + " result-4"]
261+
252−
262+ if not completer:
263+ return
264+
265+ try:
266+ results = completer(query)
267+ except Exception, e:
268+ print e
269+ results = []
270+
253 if request_data.get('format') == 'x-suggestions':
271 if request_data.get('format') == 'x-suggestions':
254− return Response(json.dumps([autocompleter.querry,autocompleter.results]),
272+ return Response(json.dumps([query, results]),
255− mimetype='application/json')
273+ mimetype='application/json')
256 else:
274 else:
257− return Response(json.dumps(autocompleter.results),
275+ return Response(json.dumps(results),
258− mimetype='application/json')
276+ mimetype='application/json')
259
277
260
278
261@app.route('/preferences', methods=['GET', 'POST'])
279@app.route('/preferences', methods=['GET', 'POST'])
@@ -276,6 +294,7 @@ def preferences():
276 else:
294 else:
277 selected_categories = []
295 selected_categories = []
278 locale = None
296 locale = None
297+ autocomplete = ''
279 for pd_name, pd in request.form.items():
298 for pd_name, pd in request.form.items():
280 if pd_name.startswith('category_'):
299 if pd_name.startswith('category_'):
281 category = pd_name[9:]
300 category = pd_name[9:]
@@ -284,6 +303,8 @@ def preferences():
284 selected_categories.append(category)
303 selected_categories.append(category)
285 elif pd_name == 'locale' and pd in settings['locales']:
304 elif pd_name == 'locale' and pd in settings['locales']:
286 locale = pd
305 locale = pd
306+ elif pd_name == 'autocomplete':
307+ autocomplete = pd
287 elif pd_name == 'language' and (pd == 'all' or
308 elif pd_name == 'language' and (pd == 'all' or
288 pd in (x[0] for
309 pd in (x[0] for
289 x in language_codes)):
310 x in language_codes)):
@@ -319,8 +340,14 @@ def preferences():
319 # cookie max age: 4 weeks
340 # cookie max age: 4 weeks
320 resp.set_cookie(
341 resp.set_cookie(
321 'categories', ','.join(selected_categories),
342 'categories', ','.join(selected_categories),
322− max_age=60 * 60 * 24 * 7 * 4
343+ max_age=cookie_max_age
323 )
344 )
345+
346+ resp.set_cookie(
347+ 'autocomplete', autocomplete,
348+ max_age=cookie_max_age
349+ )
350+
324 return resp
351 return resp
325 return render('preferences.html',
352 return render('preferences.html',
326 client=settings.get('client', None),
353 client=settings.get('client', None),
@@ -330,6 +357,7 @@ def preferences():
330 language_codes=language_codes,
357 language_codes=language_codes,
331 categs=categories.items(),
358 categs=categories.items(),
332 blocked_engines=blocked_engines,
359 blocked_engines=blocked_engines,
360+ autocomplete_backends=autocomplete_backends,
333 shortcuts={y: x for x, y in engine_shortcuts.items()})
361 shortcuts={y: x for x, y in engine_shortcuts.items()})
334
362
335
363
@@ -361,7 +389,12 @@ def opensearch():
361 # chrome/chromium only supports HTTP GET....
389 # chrome/chromium only supports HTTP GET....
362 if request.headers.get('User-Agent', '').lower().find('webkit') >= 0:
390 if request.headers.get('User-Agent', '').lower().find('webkit') >= 0:
363 method = 'get'
391 method = 'get'
392+
364− ret = render('opensearch.xml', method=method, host=get_base_url(),client=settings['client'])
393+ ret = render('opensearch.xml',
394+ method=method,
395+ host=get_base_url(),
396+ client=settings['client'])
397+
365 resp = Response(response=ret,
398 resp = Response(response=ret,
366 status=200,
399 status=200,
367 mimetype="application/xml")
400 mimetype="application/xml")