Merge pull request #2326 from return42/ungrouped

[mod] clarify the difference of the default category and subgrouping
This commit is contained in:
Markus Heiser 2023-04-07 13:34:24 +02:00 committed by GitHub
commit 393e14965a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 56 additions and 36 deletions

View file

@ -311,7 +311,6 @@ Global Settings
``results_on_new_tab``: ``results_on_new_tab``:
Open result links in a new tab by default. Open result links in a new tab by default.
.. _settings redis: .. _settings redis:
``redis:`` ``redis:``
@ -457,6 +456,9 @@ Communication with search engines.
``max_redirects`` : ``max_redirects`` :
30 by default. Maximum redirect before it is an error. 30 by default. Maximum redirect before it is an error.
.. _settings categories_as_tabs:
``categories_as_tabs:`` ``categories_as_tabs:``
----------------------- -----------------------
@ -477,6 +479,15 @@ Categories not listed here can still be searched with the :ref:`search-syntax`.
files: files:
social media: social media:
Engines are added to ``categories:`` (compare :ref:`engine categories`), the
categories listed in ``categories_as_tabs`` are shown as tabs in the UI. If
there are no active engines in a category, the tab is not displayed (e.g. if a
user disables all engines in a category).
On the preferences page (``/preferences``) -- under *engines* -- there is an
additional tab, called *other*. In this tab are all engines listed that are not
in one of the UI tabs (not included in ``categories_as_tabs``).
.. _settings engine: .. _settings engine:
Engine settings Engine settings
@ -552,10 +563,21 @@ engine is shown. Most of the options have a default value or even are optional.
to build and send a ``Accept-Language`` header in the request to the origin to build and send a ``Accept-Language`` header in the request to the origin
search engine. search engine.
.. _engine categories:
``categories`` : optional ``categories`` : optional
Define in which categories this engine will be active. Most of the time, it is Specifies to which categories the engine should be added. Engines can be
defined in the code of the engine, but in a few cases it is useful, like when assigned to multiple categories.
describing multiple search engine using the same code.
Categories can be shown as tabs (:ref:`settings categories_as_tabs`) in the
UI. A search in a tab (in the UI) will query all engines that are active in
this tab. In the preferences page (``/preferences``) -- under *engines* --
users can select what engine should be active when querying in this tab.
Alternatively, :ref:`\!bang <search-syntax>` can be used to search all engines
in a category, regardless of whether they are active or not, or whether they
are in a tab of the UI or not. For example, ``!dictionaries`` can be used to
query all search engines in that category (group).
``timeout`` : optional ``timeout`` : optional
Timeout of the search with the current search engine. **Be careful, it will Timeout of the search with the current search engine. **Be careful, it will

View file

@ -46,7 +46,7 @@ Engine File
======================= =========== ======================================================== ======================= =========== ========================================================
argument type information argument type information
======================= =========== ======================================================== ======================= =========== ========================================================
categories list pages, in which the engine is working categories list categories, in which the engine is working
paging boolean support multiple pages paging boolean support multiple pages
time_range_support boolean support search time range time_range_support boolean support search time range
engine_type str - ``online`` :ref:`[ref] <demo online engine>` by engine_type str - ``online`` :ref:`[ref] <demo online engine>` by

View file

@ -81,7 +81,7 @@ class Engine: # pylint: disable=too-few-public-methods
# settings.yml # settings.yml
categories: List[str] categories: List[str]
"""Tabs, in which the engine is working.""" """Specifies to which :ref:`engine categories` the engine should be added."""
name: str name: str
"""Name that will be used across SearXNG to define this engine. In settings, on """Name that will be used across SearXNG to define this engine. In settings, on

View file

@ -45,7 +45,7 @@ ENGINE_DEFAULT_ARGS = {
"about": {}, "about": {},
} }
# set automatically when an engine does not have any tab category # set automatically when an engine does not have any tab category
OTHER_CATEGORY = 'other' DEFAULT_CATEGORY = 'other'
# Defaults for the namespace of an engine module, see :py:func:`load_engine` # Defaults for the namespace of an engine module, see :py:func:`load_engine`
@ -132,7 +132,7 @@ def load_engine(engine_data: dict) -> Optional[Engine]:
set_loggers(engine, engine_name) set_loggers(engine, engine_name)
if not any(cat in settings['categories_as_tabs'] for cat in engine.categories): if not any(cat in settings['categories_as_tabs'] for cat in engine.categories):
engine.categories.append(OTHER_CATEGORY) engine.categories.append(DEFAULT_CATEGORY)
return engine return engine

View file

@ -36,7 +36,7 @@ about = {
send_accept_language_header = True send_accept_language_header = True
# engine dependent config # engine dependent config
categories = ["others"] categories = ["weather"]
URL = "https://duckduckgo.com/js/spice/forecast/{query}/{lang}" URL = "https://duckduckgo.com/js/spice/forecast/{query}/{lang}"

View file

@ -15,7 +15,7 @@ about = {
"results": "JSON", "results": "JSON",
} }
categories = ["others"] categories = ["weather"]
url = "https://wttr.in/{query}?format=j1&lang={lang}" url = "https://wttr.in/{query}?format=j1&lang={lang}"

View file

@ -17,7 +17,7 @@ from searx.enginelib import Engine
from searx.plugins import Plugin from searx.plugins import Plugin
from searx.locales import LOCALE_NAMES from searx.locales import LOCALE_NAMES
from searx.webutils import VALID_LANGUAGE_CODE from searx.webutils import VALID_LANGUAGE_CODE
from searx.engines import OTHER_CATEGORY from searx.engines import DEFAULT_CATEGORY
COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5 # 5 years COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5 # 5 years
@ -259,7 +259,7 @@ class EnginesSetting(BooleanChoices):
choices = {} choices = {}
for engine in engines: for engine in engines:
for category in engine.categories: for category in engine.categories:
if not category in list(settings['categories_as_tabs'].keys()) + [OTHER_CATEGORY]: if not category in list(settings['categories_as_tabs'].keys()) + [DEFAULT_CATEGORY]:
continue continue
choices['{}__{}'.format(engine.name, category)] = not engine.disabled choices['{}__{}'.format(engine.name, category)] = not engine.disabled
super().__init__(default_value, choices) super().__init__(default_value, choices)

View file

@ -15,8 +15,8 @@ __all__ = [
CONSTANT_NAMES = { CONSTANT_NAMES = {
# Constants defined in other modules # Constants defined in other modules
'DEFAULT_GROUP_NAME': webutils.DEFAULT_GROUP_NAME, 'NO_SUBGROUPING': webutils.NO_SUBGROUPING,
'OTHER_CATEGORY': engines.OTHER_CATEGORY, 'DEFAULT_CATEGORY': engines.DEFAULT_CATEGORY,
} }
CATEGORY_NAMES = { CATEGORY_NAMES = {

View file

@ -298,18 +298,18 @@
<p>{{ _('Currently used search engines') }}</p> <p>{{ _('Currently used search engines') }}</p>
{{ tabs_open() }} {{ tabs_open() }}
{% set ns = namespace(checked=true) %} {% set ns = namespace(checked=true) %}
{% for categ in categories_as_tabs + [OTHER_CATEGORY] %} {% for categ in categories_as_tabs + [DEFAULT_CATEGORY] %}
{{ tab_header('enginetab', 'category' + categ, _(categ), ns.checked )}} {{ tab_header('enginetab', 'category' + categ, _(categ), ns.checked )}}
{% set ns.checked = false %} {% set ns.checked = false %}
{% if categ == OTHER_CATEGORY %} {% if categ == DEFAULT_CATEGORY %}
<p>{{_('This tab does not show up for search results, but you can search the engines listed here via bangs.')}}</p> <p>{{_('This tab dues not exists in the user interface, but you can search in these engines by its !bangs.')}} <a href="{{ url_for('info', pagename='search-syntax') }}">&#9432;</a></p>
{% endif %} {% endif %}
<div class="scrollx"> <div class="scrollx">
<table class="striped table_engines"> <table class="striped table_engines">
<tr>{{- "" -}} <tr>{{- "" -}}
<th class="engine_checkbox">{{ _("Allow") }}</th>{{- "" -}} <th class="engine_checkbox">{{ _("Allow") }}</th>{{- "" -}}
<th class="name">{{ _("Engine name") }}</th>{{- "" -}} <th class="name">{{ _("Engine name") }}</th>{{- "" -}}
<th class="shortcut">{{ _("Shortcut") }}</th>{{- "" -}} <th class="shortcut">{{ _("!bang") }}</th>{{- "" -}}
<th>{{ _("Supports selected language") }}</th>{{- "" -}} <th>{{ _("Supports selected language") }}</th>{{- "" -}}
<th>{{ _("SafeSearch") }}</th>{{- "" -}} <th>{{ _("SafeSearch") }}</th>{{- "" -}}
<th>{{ _("Time range") }}</th>{{- "" -}} <th>{{ _("Time range") }}</th>{{- "" -}}

View file

@ -63,7 +63,7 @@ from searx.settings_defaults import OUTPUT_FORMATS
from searx.settings_loader import get_default_settings_path from searx.settings_loader import get_default_settings_path
from searx.exceptions import SearxParameterException from searx.exceptions import SearxParameterException
from searx.engines import ( from searx.engines import (
OTHER_CATEGORY, DEFAULT_CATEGORY,
categories, categories,
engines, engines,
engine_shortcuts, engine_shortcuts,
@ -435,7 +435,7 @@ def render(template_name: str, **kwargs):
kwargs['method'] = request.preferences.get_value('method') kwargs['method'] = request.preferences.get_value('method')
kwargs['categories_as_tabs'] = list(settings['categories_as_tabs'].keys()) kwargs['categories_as_tabs'] = list(settings['categories_as_tabs'].keys())
kwargs['categories'] = _get_enable_categories(categories.keys()) kwargs['categories'] = _get_enable_categories(categories.keys())
kwargs['OTHER_CATEGORY'] = OTHER_CATEGORY kwargs['DEFAULT_CATEGORY'] = DEFAULT_CATEGORY
# i18n # i18n
kwargs['sxng_locales'] = [l for l in sxng_locales if l[0] in settings['search']['languages']] kwargs['sxng_locales'] = [l for l in sxng_locales if l[0] in settings['search']['languages']]

View file

@ -18,7 +18,7 @@ from codecs import getincrementalencoder
from flask_babel import gettext, format_date from flask_babel import gettext, format_date
from searx import logger, settings from searx import logger, settings
from searx.engines import OTHER_CATEGORY from searx.engines import DEFAULT_CATEGORY
if TYPE_CHECKING: if TYPE_CHECKING:
from searx.enginelib import Engine from searx.enginelib import Engine
@ -222,26 +222,24 @@ def is_flask_run_cmdline():
return frames[-2].filename.endswith('flask/cli.py') return frames[-2].filename.endswith('flask/cli.py')
DEFAULT_GROUP_NAME = 'others' NO_SUBGROUPING = 'without further subgrouping'
def group_engines_in_tab(engines: Iterable[Engine]) -> List[Tuple[str, Iterable[Engine]]]: def group_engines_in_tab(engines: Iterable[Engine]) -> List[Tuple[str, Iterable[Engine]]]:
"""Groups an Iterable of engines by their first non tab category""" """Groups an Iterable of engines by their first non tab category (first subgroup)"""
def get_group(eng): def get_subgroup(eng):
non_tab_categories = [ non_tab_categories = [c for c in eng.categories if c not in tabs + [DEFAULT_CATEGORY]]
c for c in eng.categories if c not in list(settings['categories_as_tabs'].keys()) + [OTHER_CATEGORY] return non_tab_categories[0] if len(non_tab_categories) > 0 else NO_SUBGROUPING
]
return non_tab_categories[0] if len(non_tab_categories) > 0 else DEFAULT_GROUP_NAME
groups = itertools.groupby(sorted(engines, key=get_group), get_group)
def group_sort_key(group): def group_sort_key(group):
return (group[0] == DEFAULT_GROUP_NAME, group[0].lower()) return (group[0] == NO_SUBGROUPING, group[0].lower())
sorted_groups = sorted(((name, list(engines)) for name, engines in groups), key=group_sort_key)
def engine_sort_key(engine): def engine_sort_key(engine):
return (engine.about.get('language', ''), engine.name) return (engine.about.get('language', ''), engine.name)
tabs = list(settings['categories_as_tabs'].keys())
subgroups = itertools.groupby(sorted(engines, key=get_subgroup), get_subgroup)
sorted_groups = sorted(((name, list(engines)) for name, engines in subgroups), key=group_sort_key)
return [(groupname, sorted(engines, key=engine_sort_key)) for groupname, engines in sorted_groups] return [(groupname, sorted(engines, key=engine_sort_key)) for groupname, engines in sorted_groups]