Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
searx-engine
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
gargantext
searx-engine
Commits
971ed0ab
Commit
971ed0ab
authored
Nov 19, 2016
by
Adam Tauber
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[enh] add quick answer functionality with an example answerer
parent
55dc5383
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
156 additions
and
4 deletions
+156
-4
__init__.py
searx/answerers/__init__.py
+46
-0
answerer.py
searx/answerers/random/answerer.py
+50
-0
results.py
searx/results.py
+5
-4
search.py
searx/search.py
+8
-0
preferences.html
searx/templates/oscar/preferences.html
+29
-0
webapp.py
searx/webapp.py
+2
-0
test_answerers.py
tests/unit/test_answerers.py
+16
-0
No files found.
searx/answerers/__init__.py
0 → 100644
View file @
971ed0ab
from
os
import
listdir
from
os.path
import
realpath
,
dirname
,
join
,
isdir
from
searx.utils
import
load_module
from
collections
import
defaultdict
answerers_dir
=
dirname
(
realpath
(
__file__
))
def
load_answerers
():
answerers
=
[]
for
filename
in
listdir
(
answerers_dir
):
if
not
isdir
(
join
(
answerers_dir
,
filename
)):
continue
module
=
load_module
(
'answerer.py'
,
join
(
answerers_dir
,
filename
))
if
not
hasattr
(
module
,
'keywords'
)
or
not
isinstance
(
module
.
keywords
,
tuple
)
or
not
len
(
module
.
keywords
):
exit
(
2
)
answerers
.
append
(
module
)
return
answerers
def
get_answerers_by_keywords
(
answerers
):
by_keyword
=
defaultdict
(
list
)
for
answerer
in
answerers
:
for
keyword
in
answerer
.
keywords
:
for
keyword
in
answerer
.
keywords
:
by_keyword
[
keyword
]
.
append
(
answerer
.
answer
)
return
by_keyword
def
ask
(
query
):
results
=
[]
query_parts
=
filter
(
None
,
query
.
query
.
split
())
if
query_parts
[
0
]
not
in
answerers_by_keywords
:
return
results
for
answerer
in
answerers_by_keywords
[
query_parts
[
0
]]:
result
=
answerer
(
query
)
if
result
:
results
.
append
(
result
)
return
results
answerers
=
load_answerers
()
answerers_by_keywords
=
get_answerers_by_keywords
(
answerers
)
searx/answerers/random/answerer.py
0 → 100644
View file @
971ed0ab
import
random
import
string
from
flask_babel
import
gettext
# required answerer attribute
# specifies which search query keywords triggers this answerer
keywords
=
(
'random'
,)
random_int_max
=
2
**
31
random_string_letters
=
string
.
lowercase
+
string
.
digits
+
string
.
uppercase
def
random_string
():
return
u''
.
join
(
random
.
choice
(
random_string_letters
)
for
_
in
range
(
random
.
randint
(
8
,
32
)))
def
random_float
():
return
unicode
(
random
.
random
())
def
random_int
():
return
unicode
(
random
.
randint
(
-
random_int_max
,
random_int_max
))
random_types
=
{
u'string'
:
random_string
,
u'int'
:
random_int
,
u'float'
:
random_float
}
# required answerer function
# can return a list of results (any result type) for a given query
def
answer
(
query
):
parts
=
query
.
query
.
split
()
if
len
(
parts
)
!=
2
:
return
[]
if
parts
[
1
]
not
in
random_types
:
return
[]
return
[{
'answer'
:
random_types
[
parts
[
1
]]()}]
# required answerer function
# returns information about the answerer
def
self_info
():
return
{
'name'
:
gettext
(
'Random value generator'
),
'description'
:
gettext
(
'Generate different random values'
),
'examples'
:
[
u'random {}'
.
format
(
x
)
for
x
in
random_types
]}
searx/results.py
View file @
971ed0ab
...
@@ -146,16 +146,17 @@ class ResultContainer(object):
...
@@ -146,16 +146,17 @@ class ResultContainer(object):
self
.
_number_of_results
.
append
(
result
[
'number_of_results'
])
self
.
_number_of_results
.
append
(
result
[
'number_of_results'
])
results
.
remove
(
result
)
results
.
remove
(
result
)
with
RLock
():
if
engine_name
in
engines
:
engines
[
engine_name
]
.
stats
[
'search_count'
]
+=
1
with
RLock
():
engines
[
engine_name
]
.
stats
[
'result_count'
]
+=
len
(
results
)
engines
[
engine_name
]
.
stats
[
'search_count'
]
+=
1
engines
[
engine_name
]
.
stats
[
'result_count'
]
+=
len
(
results
)
if
not
results
:
if
not
results
:
return
return
self
.
results
[
engine_name
]
.
extend
(
results
)
self
.
results
[
engine_name
]
.
extend
(
results
)
if
not
self
.
paging
and
engines
[
engine_name
]
.
paging
:
if
not
self
.
paging
and
engine
_name
in
engines
and
engine
s
[
engine_name
]
.
paging
:
self
.
paging
=
True
self
.
paging
=
True
for
i
,
result
in
enumerate
(
results
):
for
i
,
result
in
enumerate
(
results
):
...
...
searx/search.py
View file @
971ed0ab
...
@@ -24,6 +24,7 @@ import searx.poolrequests as requests_lib
...
@@ -24,6 +24,7 @@ import searx.poolrequests as requests_lib
from
searx.engines
import
(
from
searx.engines
import
(
categories
,
engines
categories
,
engines
)
)
from
searx.answerers
import
ask
from
searx.utils
import
gen_useragent
from
searx.utils
import
gen_useragent
from
searx.query
import
RawTextQuery
,
SearchQuery
from
searx.query
import
RawTextQuery
,
SearchQuery
from
searx.results
import
ResultContainer
from
searx.results
import
ResultContainer
...
@@ -254,6 +255,13 @@ class Search(object):
...
@@ -254,6 +255,13 @@ class Search(object):
def
search
(
self
):
def
search
(
self
):
global
number_of_searches
global
number_of_searches
answerers_results
=
ask
(
self
.
search_query
)
if
answerers_results
:
for
results
in
answerers_results
:
self
.
result_container
.
extend
(
'answer'
,
results
)
return
self
.
result_container
# init vars
# init vars
requests
=
[]
requests
=
[]
...
...
searx/templates/oscar/preferences.html
View file @
971ed0ab
...
@@ -12,6 +12,7 @@
...
@@ -12,6 +12,7 @@
<li
class=
"active"
><a
href=
"#tab_general"
role=
"tab"
data-toggle=
"tab"
>
{{ _('General') }}
</a></li>
<li
class=
"active"
><a
href=
"#tab_general"
role=
"tab"
data-toggle=
"tab"
>
{{ _('General') }}
</a></li>
<li><a
href=
"#tab_engine"
role=
"tab"
data-toggle=
"tab"
>
{{ _('Engines') }}
</a></li>
<li><a
href=
"#tab_engine"
role=
"tab"
data-toggle=
"tab"
>
{{ _('Engines') }}
</a></li>
<li><a
href=
"#tab_plugins"
role=
"tab"
data-toggle=
"tab"
>
{{ _('Plugins') }}
</a></li>
<li><a
href=
"#tab_plugins"
role=
"tab"
data-toggle=
"tab"
>
{{ _('Plugins') }}
</a></li>
{% if answerers %}
<li><a
href=
"#tab_answerers"
role=
"tab"
data-toggle=
"tab"
>
{{ _('Answerers') }}
</a></li>
{% endif %}
<li><a
href=
"#tab_cookies"
role=
"tab"
data-toggle=
"tab"
>
{{ _('Cookies') }}
</a></li>
<li><a
href=
"#tab_cookies"
role=
"tab"
data-toggle=
"tab"
>
{{ _('Cookies') }}
</a></li>
</ul>
</ul>
...
@@ -224,6 +225,34 @@
...
@@ -224,6 +225,34 @@
</fieldset>
</fieldset>
</div>
</div>
{% if answerers %}
<div
class=
"tab-pane active_if_nojs"
id=
"tab_answerers"
>
<noscript>
<h3>
{{ _('Answerers') }}
</h3>
</noscript>
<p
class=
"text-muted"
style=
"margin:20px 0;"
>
{{ _('This is the list of searx\'s instant answering modules.') }}
</p>
<table
class=
"table table-striped"
>
<tr>
<th
class=
"text-muted"
>
{{ _('Name') }}
</th>
<th
class=
"text-muted"
>
{{ _('Keywords') }}
</th>
<th
class=
"text-muted"
>
{{ _('Description') }}
</th>
<th
class=
"text-muted"
>
{{ _('Examples') }}
</th>
</tr>
{% for answerer in answerers %}
<tr>
<td
class=
"text-muted"
>
{{ answerer.info.name }}
</td>
<td
class=
"text-muted"
>
{{ answerer.keywords|join(', ') }}
</td>
<td
class=
"text-muted"
>
{{ answerer.info.description }}
</td>
<td
class=
"text-muted"
>
{{ answerer.info.examples|join(', ') }}
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
<div
class=
"tab-pane active_if_nojs"
id=
"tab_cookies"
>
<div
class=
"tab-pane active_if_nojs"
id=
"tab_cookies"
>
<noscript>
<noscript>
<h3>
{{ _('Cookies') }}
</h3>
<h3>
{{ _('Cookies') }}
</h3>
...
...
searx/webapp.py
View file @
971ed0ab
...
@@ -67,6 +67,7 @@ from searx.query import RawTextQuery
...
@@ -67,6 +67,7 @@ from searx.query import RawTextQuery
from
searx.autocomplete
import
searx_bang
,
backends
as
autocomplete_backends
from
searx.autocomplete
import
searx_bang
,
backends
as
autocomplete_backends
from
searx.plugins
import
plugins
from
searx.plugins
import
plugins
from
searx.preferences
import
Preferences
,
ValidationException
from
searx.preferences
import
Preferences
,
ValidationException
from
searx.answerers
import
answerers
# check if the pyopenssl, ndg-httpsclient, pyasn1 packages are installed.
# check if the pyopenssl, ndg-httpsclient, pyasn1 packages are installed.
# They are needed for SSL connection without trouble, see #298
# They are needed for SSL connection without trouble, see #298
...
@@ -612,6 +613,7 @@ def preferences():
...
@@ -612,6 +613,7 @@ def preferences():
language_codes
=
language_codes
,
language_codes
=
language_codes
,
engines_by_category
=
categories
,
engines_by_category
=
categories
,
stats
=
stats
,
stats
=
stats
,
answerers
=
[{
'info'
:
a
.
self_info
(),
'keywords'
:
a
.
keywords
}
for
a
in
answerers
],
disabled_engines
=
disabled_engines
,
disabled_engines
=
disabled_engines
,
autocomplete_backends
=
autocomplete_backends
,
autocomplete_backends
=
autocomplete_backends
,
shortcuts
=
{
y
:
x
for
x
,
y
in
engine_shortcuts
.
items
()},
shortcuts
=
{
y
:
x
for
x
,
y
in
engine_shortcuts
.
items
()},
...
...
tests/unit/test_answerers.py
0 → 100644
View file @
971ed0ab
# -*- coding: utf-8 -*-
from
mock
import
Mock
from
searx.answerers
import
answerers
from
searx.testing
import
SearxTestCase
class
AnswererTest
(
SearxTestCase
):
def
test_unicode_input
(
self
):
query
=
Mock
()
unicode_payload
=
u'árvíztűrő tükörfúrógép'
for
answerer
in
answerers
:
query
.
query
=
u'{} {}'
.
format
(
answerer
.
keywords
[
0
],
unicode_payload
)
self
.
assertTrue
(
isinstance
(
answerer
.
answer
(
query
),
list
))
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment