forked from zaclys/searxng
[feat] implement feeling lucky feature
This commit is contained in:
parent
31aa1c7c33
commit
b21f8a6751
|
@ -150,7 +150,7 @@ class LanguageParser(QueryPartParser):
|
|||
class ExternalBangParser(QueryPartParser):
|
||||
@staticmethod
|
||||
def check(raw_value):
|
||||
return raw_value.startswith('!!')
|
||||
return raw_value.startswith('!!') and len(raw_value) > 2
|
||||
|
||||
def __call__(self, raw_value):
|
||||
value = raw_value[2:]
|
||||
|
@ -177,7 +177,8 @@ class ExternalBangParser(QueryPartParser):
|
|||
class BangParser(QueryPartParser):
|
||||
@staticmethod
|
||||
def check(raw_value):
|
||||
return raw_value[0] == '!'
|
||||
# make sure it's not any bang with double '!!'
|
||||
return raw_value[0] == '!' and (len(raw_value) < 2 or raw_value[1] != '!')
|
||||
|
||||
def __call__(self, raw_value):
|
||||
value = raw_value[1:].replace('-', ' ').replace('_', ' ')
|
||||
|
@ -235,14 +236,25 @@ class BangParser(QueryPartParser):
|
|||
self._add_autocomplete(first_char + engine_shortcut)
|
||||
|
||||
|
||||
class FeelingLuckyParser(QueryPartParser):
|
||||
@staticmethod
|
||||
def check(raw_value):
|
||||
return raw_value == '!!'
|
||||
|
||||
def __call__(self, raw_value):
|
||||
self.raw_text_query.redirect_to_first_result = True
|
||||
return True
|
||||
|
||||
|
||||
class RawTextQuery:
|
||||
"""parse raw text query (the value from the html input)"""
|
||||
|
||||
PARSER_CLASSES = [
|
||||
TimeoutParser, # this force the timeout
|
||||
LanguageParser, # this force a language
|
||||
TimeoutParser, # force the timeout
|
||||
LanguageParser, # force a language
|
||||
ExternalBangParser, # external bang (must be before BangParser)
|
||||
BangParser, # this force a engine or category
|
||||
BangParser, # force an engine or category
|
||||
FeelingLuckyParser, # redirect to the first link in the results list
|
||||
]
|
||||
|
||||
def __init__(self, query, disabled_engines):
|
||||
|
@ -261,6 +273,7 @@ class RawTextQuery:
|
|||
self.query_parts = [] # use self.getFullQuery()
|
||||
self.user_query_parts = [] # use self.getQuery()
|
||||
self.autocomplete_location = None
|
||||
self.redirect_to_first_result = False
|
||||
self._parse_query()
|
||||
|
||||
def _parse_query(self):
|
||||
|
@ -330,5 +343,6 @@ class RawTextQuery:
|
|||
+ f"enginerefs={self.enginerefs!r}\n "
|
||||
+ f"autocomplete_list={self.autocomplete_list!r}\n "
|
||||
+ f"query_parts={self.query_parts!r}\n "
|
||||
+ f"user_query_parts={self.user_query_parts!r} >"
|
||||
+ f"user_query_parts={self.user_query_parts!r} >\n"
|
||||
+ f"redirect_to_first_result={self.redirect_to_first_result!r}"
|
||||
)
|
||||
|
|
|
@ -37,6 +37,7 @@ class SearchQuery:
|
|||
'timeout_limit',
|
||||
'external_bang',
|
||||
'engine_data',
|
||||
'redirect_to_first_result',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
@ -50,6 +51,7 @@ class SearchQuery:
|
|||
timeout_limit: typing.Optional[float] = None,
|
||||
external_bang: typing.Optional[str] = None,
|
||||
engine_data: typing.Optional[typing.Dict[str, str]] = None,
|
||||
redirect_to_first_result: typing.Optional[bool] = None,
|
||||
):
|
||||
self.query = query
|
||||
self.engineref_list = engineref_list
|
||||
|
@ -60,6 +62,7 @@ class SearchQuery:
|
|||
self.timeout_limit = timeout_limit
|
||||
self.external_bang = external_bang
|
||||
self.engine_data = engine_data or {}
|
||||
self.redirect_to_first_result = redirect_to_first_result
|
||||
|
||||
self.locale = None
|
||||
if self.lang:
|
||||
|
@ -73,7 +76,7 @@ class SearchQuery:
|
|||
return list(set(map(lambda engineref: engineref.category, self.engineref_list)))
|
||||
|
||||
def __repr__(self):
|
||||
return "SearchQuery({!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r})".format(
|
||||
return "SearchQuery({!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r})".format(
|
||||
self.query,
|
||||
self.engineref_list,
|
||||
self.lang,
|
||||
|
@ -82,6 +85,7 @@ class SearchQuery:
|
|||
self.time_range,
|
||||
self.timeout_limit,
|
||||
self.external_bang,
|
||||
self.redirect_to_first_result,
|
||||
)
|
||||
|
||||
def __eq__(self, other):
|
||||
|
@ -94,6 +98,7 @@ class SearchQuery:
|
|||
and self.time_range == other.time_range
|
||||
and self.timeout_limit == other.timeout_limit
|
||||
and self.external_bang == other.external_bang
|
||||
and self.redirect_to_first_result == other.redirect_to_first_result
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -107,6 +112,7 @@ class SearchQuery:
|
|||
self.time_range,
|
||||
self.timeout_limit,
|
||||
self.external_bang,
|
||||
self.redirect_to_first_result,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -121,4 +127,5 @@ class SearchQuery:
|
|||
self.timeout_limit,
|
||||
self.external_bang,
|
||||
self.engine_data,
|
||||
self.redirect_to_first_result,
|
||||
)
|
||||
|
|
|
@ -254,6 +254,7 @@ def get_search_query_from_webapp(
|
|||
query_time_range = parse_time_range(form)
|
||||
query_timeout = parse_timeout(form, raw_text_query)
|
||||
external_bang = raw_text_query.external_bang
|
||||
redirect_to_first_result = raw_text_query.redirect_to_first_result
|
||||
engine_data = parse_engine_data(form)
|
||||
|
||||
query_lang = parse_lang(preferences, form, raw_text_query)
|
||||
|
@ -288,6 +289,7 @@ def get_search_query_from_webapp(
|
|||
query_timeout,
|
||||
external_bang=external_bang,
|
||||
engine_data=engine_data,
|
||||
redirect_to_first_result=redirect_to_first_result,
|
||||
),
|
||||
raw_text_query,
|
||||
query_engineref_list_unknown,
|
||||
|
|
|
@ -697,6 +697,10 @@ def search():
|
|||
previous_result = None
|
||||
|
||||
results = result_container.get_ordered_results()
|
||||
|
||||
if search_query.redirect_to_first_result and results:
|
||||
return redirect(results[0]['url'], 302)
|
||||
|
||||
for result in results:
|
||||
if output_format == 'html':
|
||||
if 'content' in result and result['content']:
|
||||
|
|
|
@ -225,18 +225,6 @@ class TestExternalBangParser(SearxTestCase):
|
|||
a = query.autocomplete_list[0]
|
||||
self.assertEqual(query.get_autocomplete_full_query(a), a + ' the query')
|
||||
|
||||
def test_external_bang_autocomplete_empty(self):
|
||||
query_text = 'the query !!'
|
||||
query = RawTextQuery(query_text, [])
|
||||
|
||||
self.assertEqual(query.getFullQuery(), 'the query !!')
|
||||
self.assertEqual(len(query.query_parts), 0)
|
||||
self.assertFalse(query.specific)
|
||||
self.assertGreater(len(query.autocomplete_list), 2)
|
||||
|
||||
a = query.autocomplete_list[0]
|
||||
self.assertEqual(query.get_autocomplete_full_query(a), 'the query ' + a)
|
||||
|
||||
|
||||
class TestBang(SearxTestCase):
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class SearchQueryTestCase(SearxTestCase):
|
|||
def test_repr(self):
|
||||
s = SearchQuery('test', [EngineRef('bing', 'general')], 'all', 0, 1, '1', 5.0, 'g')
|
||||
self.assertEqual(
|
||||
repr(s), "SearchQuery('test', [EngineRef('bing', 'general')], 'all', 0, 1, '1', 5.0, 'g')"
|
||||
repr(s), "SearchQuery('test', [EngineRef('bing', 'general')], 'all', 0, 1, '1', 5.0, 'g', None)"
|
||||
) # noqa
|
||||
|
||||
def test_eq(self):
|
||||
|
|
Loading…
Reference in New Issue