|
@@ -28,10 +28,12 @@ from flask import (
|
28
|
28
|
from flask.ext.babel import Babel
|
29
|
29
|
from searx import settings, searx_dir
|
30
|
30
|
from searx.engines import (
|
31
|
|
- search, categories, engines, get_engines_stats, engine_shortcuts
|
|
31
|
+ search as do_search, categories, engines, get_engines_stats,
|
|
32
|
+ engine_shortcuts
|
32
|
33
|
)
|
33
|
34
|
from searx.utils import UnicodeWriter, highlight_content, html_to_text
|
34
|
35
|
from searx.languages import language_codes
|
|
36
|
+from searx.search import Search
|
35
|
37
|
|
36
|
38
|
|
37
|
39
|
app = Flask(
|
|
@@ -94,95 +96,32 @@ def render(template_name, **kwargs):
|
94
|
96
|
return render_template(template_name, **kwargs)
|
95
|
97
|
|
96
|
98
|
|
97
|
|
-def parse_query(query):
|
98
|
|
- query_engines = []
|
99
|
|
- query_parts = query.split()
|
100
|
|
-
|
101
|
|
- if query_parts[0].startswith('!'):
|
102
|
|
- prefix = query_parts[0][1:].replace('_', ' ')
|
103
|
|
- if prefix in engine_shortcuts:
|
104
|
|
- query_engines.append({'category': 'none',
|
105
|
|
- 'name': engine_shortcuts[prefix]})
|
106
|
|
- elif prefix in engines:
|
107
|
|
- query_engines.append({'category': 'none',
|
108
|
|
- 'name': prefix})
|
109
|
|
- elif prefix in categories:
|
110
|
|
- query_engines.extend({'category': prefix,
|
111
|
|
- 'name': engine.name}
|
112
|
|
- for engine in categories[prefix])
|
113
|
|
-
|
114
|
|
- if len(query_engines):
|
115
|
|
- query = query.replace(query_parts[0], '', 1).strip()
|
116
|
|
- return query, query_engines
|
117
|
|
-
|
118
|
|
-
|
119
|
99
|
@app.route('/', methods=['GET', 'POST'])
|
120
|
100
|
def index():
|
121
|
101
|
"""Render index page.
|
122
|
102
|
|
123
|
103
|
Supported outputs: html, json, csv, rss.
|
124
|
104
|
"""
|
125
|
|
- paging = False
|
126
|
|
- lang = 'all'
|
127
|
|
-
|
128
|
|
- if request.cookies.get('language')\
|
129
|
|
- and request.cookies['language'] in (x[0] for x in language_codes):
|
130
|
|
- lang = request.cookies['language']
|
131
|
|
-
|
132
|
|
- if request.method == 'POST':
|
133
|
|
- request_data = request.form
|
134
|
|
- else:
|
135
|
|
- request_data = request.args
|
136
|
|
- if not request_data.get('q'):
|
137
|
|
- return render('index.html')
|
138
|
105
|
|
139
|
|
- pageno_param = request_data.get('pageno', '1')
|
140
|
|
- if not pageno_param.isdigit() or int(pageno_param) < 1:
|
|
106
|
+ try:
|
|
107
|
+ search = Search(request)
|
|
108
|
+ except:
|
141
|
109
|
return render('index.html')
|
142
|
110
|
|
143
|
|
- pageno = int(pageno_param)
|
144
|
|
-
|
145
|
|
- selected_categories = []
|
146
|
|
-
|
147
|
|
- query, selected_engines = parse_query(request_data['q'].encode('utf-8'))
|
148
|
|
-
|
149
|
|
- if len(selected_engines):
|
150
|
|
- selected_categories = list(set(engine['category']
|
151
|
|
- for engine in selected_engines))
|
152
|
|
- else:
|
153
|
|
- for pd_name, pd in request_data.items():
|
154
|
|
- if pd_name.startswith('category_'):
|
155
|
|
- category = pd_name[9:]
|
156
|
|
- if not category in categories:
|
157
|
|
- continue
|
158
|
|
- selected_categories.append(category)
|
159
|
|
- if not len(selected_categories):
|
160
|
|
- cookie_categories = request.cookies.get('categories', '')
|
161
|
|
- cookie_categories = cookie_categories.split(',')
|
162
|
|
- for ccateg in cookie_categories:
|
163
|
|
- if ccateg in categories:
|
164
|
|
- selected_categories.append(ccateg)
|
165
|
|
- if not len(selected_categories):
|
166
|
|
- selected_categories = ['general']
|
167
|
|
-
|
168
|
|
- for categ in selected_categories:
|
169
|
|
- selected_engines.extend({'category': categ,
|
170
|
|
- 'name': x.name}
|
171
|
|
- for x in categories[categ])
|
172
|
|
-
|
173
|
|
- results, suggestions = search(query,
|
174
|
|
- request,
|
175
|
|
- selected_engines,
|
176
|
|
- pageno,
|
177
|
|
- lang)
|
178
|
|
-
|
179
|
|
- for result in results:
|
180
|
|
- if not paging and engines[result['engine']].paging:
|
181
|
|
- paging = True
|
182
|
|
- if request_data.get('format', 'html') == 'html':
|
|
111
|
+ # TODO moar refactor - do_search integration into Search class
|
|
112
|
+ search.results, search.suggestions = do_search(search.query,
|
|
113
|
+ request,
|
|
114
|
+ search.engines,
|
|
115
|
+ search.pageno,
|
|
116
|
+ search.lang)
|
|
117
|
+
|
|
118
|
+ for result in search.results:
|
|
119
|
+ if not search.paging and engines[result['engine']].paging:
|
|
120
|
+ search.paging = True
|
|
121
|
+ if search.request_data.get('format', 'html') == 'html':
|
183
|
122
|
if 'content' in result:
|
184
|
|
- result['content'] = highlight_content(result['content'], query)
|
185
|
|
- result['title'] = highlight_content(result['title'], query)
|
|
123
|
+ result['content'] = highlight_content(result['content'], search.query)
|
|
124
|
+ result['title'] = highlight_content(result['title'], search.query)
|
186
|
125
|
else:
|
187
|
126
|
if 'content' in result:
|
188
|
127
|
result['content'] = html_to_text(result['content']).strip()
|
|
@@ -199,40 +138,40 @@ def index():
|
199
|
138
|
if engine in favicons:
|
200
|
139
|
result['favicon'] = engine
|
201
|
140
|
|
202
|
|
- if request_data.get('format') == 'json':
|
203
|
|
- return Response(json.dumps({'query': query, 'results': results}),
|
|
141
|
+ if search.request_data.get('format') == 'json':
|
|
142
|
+ return Response(json.dumps({'query': search.query, 'results': search.results}),
|
204
|
143
|
mimetype='application/json')
|
205
|
|
- elif request_data.get('format') == 'csv':
|
|
144
|
+ elif search.request_data.get('format') == 'csv':
|
206
|
145
|
csv = UnicodeWriter(cStringIO.StringIO())
|
207
|
146
|
keys = ('title', 'url', 'content', 'host', 'engine', 'score')
|
208
|
|
- if len(results):
|
|
147
|
+ if len(search.results):
|
209
|
148
|
csv.writerow(keys)
|
210
|
|
- for row in results:
|
|
149
|
+ for row in search.results:
|
211
|
150
|
row['host'] = row['parsed_url'].netloc
|
212
|
151
|
csv.writerow([row.get(key, '') for key in keys])
|
213
|
152
|
csv.stream.seek(0)
|
214
|
153
|
response = Response(csv.stream.read(), mimetype='application/csv')
|
215
|
|
- content_disp = 'attachment;Filename=searx_-_{0}.csv'.format(query)
|
|
154
|
+ content_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search.query)
|
216
|
155
|
response.headers.add('Content-Disposition', content_disp)
|
217
|
156
|
return response
|
218
|
|
- elif request_data.get('format') == 'rss':
|
|
157
|
+ elif search.request_data.get('format') == 'rss':
|
219
|
158
|
response_rss = render(
|
220
|
159
|
'opensearch_response_rss.xml',
|
221
|
|
- results=results,
|
222
|
|
- q=request_data['q'],
|
223
|
|
- number_of_results=len(results),
|
|
160
|
+ results=search.results,
|
|
161
|
+ q=search.request_data['q'],
|
|
162
|
+ number_of_results=len(search.results),
|
224
|
163
|
base_url=get_base_url()
|
225
|
164
|
)
|
226
|
165
|
return Response(response_rss, mimetype='text/xml')
|
227
|
166
|
|
228
|
167
|
return render(
|
229
|
168
|
'results.html',
|
230
|
|
- results=results,
|
231
|
|
- q=request_data['q'],
|
232
|
|
- selected_categories=selected_categories,
|
233
|
|
- paging=paging,
|
234
|
|
- pageno=pageno,
|
235
|
|
- suggestions=suggestions
|
|
169
|
+ results=search.results,
|
|
170
|
+ q=search.request_data['q'],
|
|
171
|
+ selected_categories=search.categories,
|
|
172
|
+ paging=search.paging,
|
|
173
|
+ pageno=search.pageno,
|
|
174
|
+ suggestions=search.suggestions
|
236
|
175
|
)
|
237
|
176
|
|
238
|
177
|
|