ckanext-search-tweaks 0.4.12__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {ckanext-search-tweaks-0.4.12/ckanext_search_tweaks.egg-info → ckanext-search-tweaks-0.6.0}/PKG-INFO +7 -4
  2. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/__init__.py +0 -1
  3. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/__init__.py +3 -0
  4. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/advanced_search/plugin.py +16 -10
  5. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/cli.py +3 -3
  6. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/config.py +37 -0
  7. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/field_relevance/plugin.py +7 -10
  8. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/field_relevance/views.py +12 -19
  9. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/interfaces.py +32 -0
  10. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/plugin.py +19 -44
  11. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/query_popularity/config.py +30 -0
  12. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/query_popularity/logic/action.py +43 -0
  13. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/query_popularity/logic/auth.py +23 -0
  14. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/query_popularity/plugin.py +47 -0
  15. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/query_popularity/score.py +165 -0
  16. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/query_relevance/__init__.py +1 -2
  17. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/query_relevance/cli.py +3 -7
  18. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/query_relevance/plugin.py +18 -24
  19. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/query_relevance/score.py +1 -1
  20. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/query_relevance/storage.py +7 -14
  21. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/shared.py +13 -0
  22. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/spellcheck/helpers.py +15 -23
  23. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/spellcheck/plugin.py +1 -1
  24. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/tests/query_relevance/test_plugin.py +2 -3
  25. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/tests/query_relevance/test_storage.py +4 -4
  26. ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/tests/spellcheck/__init__.py +0 -0
  27. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/tests/spellcheck/test_plugin.py +7 -15
  28. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/tests/test_plugin.py +21 -32
  29. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0/ckanext_search_tweaks.egg-info}/PKG-INFO +7 -4
  30. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext_search_tweaks.egg-info/SOURCES.txt +9 -0
  31. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext_search_tweaks.egg-info/entry_points.txt +1 -0
  32. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext_search_tweaks.egg-info/requires.txt +1 -1
  33. ckanext-search-tweaks-0.6.0/pyproject.toml +129 -0
  34. ckanext-search-tweaks-0.6.0/requirements.txt +0 -0
  35. ckanext-search-tweaks-0.6.0/setup.cfg +66 -0
  36. ckanext-search-tweaks-0.6.0/setup.py +15 -0
  37. ckanext-search-tweaks-0.4.12/ckanext/search_tweaks/__init__.py +0 -19
  38. ckanext-search-tweaks-0.4.12/ckanext/search_tweaks/interfaces.py +0 -25
  39. ckanext-search-tweaks-0.4.12/pyproject.toml +0 -23
  40. ckanext-search-tweaks-0.4.12/setup.cfg +0 -26
  41. ckanext-search-tweaks-0.4.12/setup.py +0 -113
  42. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/LICENSE +0 -0
  43. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/MANIFEST.in +0 -0
  44. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/README.md +0 -0
  45. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/advanced_search/__init__.py +0 -0
  46. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/advanced_search/assets/advanced-search.css +0 -0
  47. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/advanced_search/assets/advanced-search.js +0 -0
  48. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/advanced_search/assets/webassets.yml +0 -0
  49. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/advanced_search/templates/advanced_search/search_form.html +0 -0
  50. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/field_relevance/__init__.py +0 -0
  51. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/field_relevance/assets/search-tweaks-reflect-range-in-label.js +0 -0
  52. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/field_relevance/assets/webassets.yml +0 -0
  53. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/field_relevance/templates/search_tweaks/field_relevance/promote.html +0 -0
  54. {ckanext-search-tweaks-0.4.12/ckanext/search_tweaks/tests → ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/query_popularity}/__init__.py +0 -0
  55. {ckanext-search-tweaks-0.4.12/ckanext/search_tweaks/tests/query_relevance → ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/query_popularity/logic}/__init__.py +0 -0
  56. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/spellcheck/__init__.py +0 -0
  57. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/spellcheck/cli.py +0 -0
  58. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/spellcheck/templates/search_tweaks/did_you_mean.html +0 -0
  59. {ckanext-search-tweaks-0.4.12/ckanext/search_tweaks/tests/spellcheck → ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/tests}/__init__.py +0 -0
  60. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/tests/conftest.py +0 -0
  61. /ckanext-search-tweaks-0.4.12/requirements.txt → /ckanext-search-tweaks-0.6.0/ckanext/search_tweaks/tests/query_relevance/__init__.py +0 -0
  62. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext/search_tweaks/tests/query_relevance/test_score.py +0 -0
  63. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext_search_tweaks.egg-info/dependency_links.txt +0 -0
  64. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext_search_tweaks.egg-info/namespace_packages.txt +0 -0
  65. {ckanext-search-tweaks-0.4.12 → ckanext-search-tweaks-0.6.0}/ckanext_search_tweaks.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ckanext-search-tweaks
3
- Version: 0.4.12
3
+ Version: 0.6.0
4
4
  Home-page: https://github.com/DataShades/ckanext-search-tweaks
5
5
  Author: Sergey Motornyuk
6
6
  Author-email: sergey.motornyuk@linkdigital.com.au
@@ -8,11 +8,14 @@ License: AGPL
8
8
  Keywords: CKAN
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
11
- Classifier: Programming Language :: Python :: 2.7
12
- Requires-Python: >=3.7
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
13
14
  Description-Content-Type: text/markdown
14
- Provides-Extra: advanced-search
15
15
  License-File: LICENSE
16
+ Requires-Dist: freezegun
17
+ Requires-Dist: typing_extensions>=4.0.0
18
+ Provides-Extra: advanced-search
16
19
 
17
20
  [![Tests](https://github.com/DataShades/ckanext-search-tweaks/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/DataShades/ckanext-search-tweaks/actions)
18
21
 
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
 
3
2
  # this is a namespace package
4
3
  try:
@@ -0,0 +1,3 @@
1
+ from __future__ import annotations
2
+
3
+ from .shared import feature_disabled
@@ -1,6 +1,8 @@
1
1
  from __future__ import annotations
2
+
2
3
  import json
3
4
  from typing import Any
5
+
4
6
  import ckan.plugins as p
5
7
  import ckan.plugins.toolkit as tk
6
8
  from ckan.exceptions import CkanConfigurationException
@@ -35,14 +37,14 @@ DEFAULT_FORM_DEFINITION = json.dumps(
35
37
  {"value": "private", "label": "Private"},
36
38
  ],
37
39
  },
38
- }
40
+ },
39
41
  )
40
42
  DEFAULT_FIELD_ORDER = None
41
43
 
42
44
 
43
45
  def form_config():
44
46
  definition = json.loads(
45
- tk.config.get(CONFIG_FORM_DEFINITION, DEFAULT_FORM_DEFINITION)
47
+ tk.config.get(CONFIG_FORM_DEFINITION, DEFAULT_FORM_DEFINITION),
46
48
  )
47
49
  order = tk.aslist(tk.config.get(CONFIG_FIELD_ORDER, DEFAULT_FIELD_ORDER))
48
50
  if not order:
@@ -72,17 +74,18 @@ class AdvancedSearchPlugin(p.SingletonPlugin):
72
74
  from ckanext.composite_search.interfaces import ICompositeSearch
73
75
  except ImportError:
74
76
  raise CkanConfigurationException(
75
- "ckanext-composite-search is not installed"
77
+ "ckanext-composite-search is not installed",
76
78
  )
77
79
  if not p.plugin_loaded("composite_search"):
78
- raise CkanConfigurationException(
79
- "`composite_search` plugin must be enabled in order to use advanced search"
80
- )
80
+ msg = "Advanced search requires `composite_search` plugin"
81
+ raise CkanConfigurationException(msg)
81
82
  if not list(p.PluginImplementations(ICompositeSearch)):
82
- raise CkanConfigurationException(
83
- "Advanced search requires plugin that implements ICompositeSearch."
84
- + " Consider enabling `default_composite_search` plugins."
83
+ msg = (
84
+ "Advanced search requires plugin that implements"
85
+ + " ICompositeSearch. Consider enabling "
86
+ + "`default_composite_search` plugins."
85
87
  )
88
+ raise CkanConfigurationException(msg)
86
89
 
87
90
  # ITemplateHelpers
88
91
 
@@ -93,9 +96,12 @@ class AdvancedSearchPlugin(p.SingletonPlugin):
93
96
 
94
97
  # IPackageController
95
98
 
96
- def before_search(self, search_params: dict[str, Any]):
99
+ def before_dataset_search(self, search_params: dict[str, Any]):
97
100
  solr_q = search_params.get("extras", {}).get("ext_solr_q", None)
98
101
  if solr_q:
99
102
  search_params.setdefault("q", "")
100
103
  search_params["q"] += " " + solr_q
101
104
  return search_params
105
+
106
+ if not tk.check_ckan_version("2.10"):
107
+ before_search = before_dataset_search
@@ -1,8 +1,8 @@
1
1
  import click
2
2
 
3
-
4
- def get_commands():
5
- return [search_tweaks]
3
+ __all__ = [
4
+ "search_tweaks",
5
+ ]
6
6
 
7
7
 
8
8
  @click.group(short_help="Search tweaks")
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ import ckan.plugins.toolkit as tk
4
+ from ckan.lib.search.query import QUERY_FIELDS
5
+
6
+ CONFIG_QF = "ckanext.search_tweaks.common.qf"
7
+ DEFAULT_QF = QUERY_FIELDS
8
+
9
+ CONFIG_FUZZY = "ckanext.search_tweaks.common.fuzzy_search.enabled"
10
+ CONFIG_FUZZY_DISTANCE = "ckanext.search_tweaks.common.fuzzy_search.distance"
11
+ CONFIG_MM = "ckanext.search_tweaks.common.mm"
12
+ CONFIG_FUZZY_KEEP_ORIGINAL = "ckanext.search_tweaks.common.fuzzy_search.keep_original"
13
+ CONFIG_PREFER_BOOST = "ckanext.search_tweaks.common.prefer_boost"
14
+
15
+
16
+ def qf() -> str:
17
+ return tk.config[CONFIG_QF] or DEFAULT_QF
18
+
19
+
20
+ def fuzzy() -> bool:
21
+ return tk.config[CONFIG_FUZZY]
22
+
23
+
24
+ def fuzzy_distance() -> int:
25
+ return tk.config[CONFIG_FUZZY_DISTANCE]
26
+
27
+
28
+ def mm() -> str:
29
+ return tk.config[CONFIG_MM]
30
+
31
+
32
+ def fuzzy_with_original() -> bool:
33
+ return tk.config[CONFIG_FUZZY_KEEP_ORIGINAL]
34
+
35
+
36
+ def prefer_boost() -> bool:
37
+ return tk.config[CONFIG_PREFER_BOOST]
@@ -1,13 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
 
5
5
  import ckan.plugins as p
6
6
  import ckan.plugins.toolkit as tk
7
7
 
8
+ from ckanext.search_tweaks import feature_disabled
9
+ from ckanext.search_tweaks.interfaces import ISearchTweaks
8
10
  from . import views
9
- from ..interfaces import ISearchTweaks
10
- from .. import feature_disabled
11
11
 
12
12
  CONFIG_BOOST_FN = "ckanext.search_tweaks.field_relevance.boost_function"
13
13
 
@@ -21,11 +21,9 @@ class FieldRelevancePlugin(p.SingletonPlugin):
21
21
  p.implements(p.IConfigurer, inherit=True)
22
22
 
23
23
  # ISearchTweaks
24
- def get_search_boost_fn(
25
- self, search_params: dict[str, Any]
26
- ) -> Optional[str]:
24
+ def get_search_boost_fn(self, search_params: dict[str, Any]) -> str | None:
27
25
  if feature_disabled("field_boost", search_params):
28
- return
26
+ return None
29
27
 
30
28
  return tk.config.get(CONFIG_BOOST_FN, DEFAULT_BOOST_FN)
31
29
 
@@ -40,15 +38,14 @@ class FieldRelevancePlugin(p.SingletonPlugin):
40
38
  tk.add_template_directory(config, "templates")
41
39
  tk.add_resource("assets", "search_tweaks_field_relevance")
42
40
 
43
-
44
41
  # IAuthFunctions
45
42
  def get_auth_functions(self):
46
43
  return {
47
- "search_tweaks_field_relevance_promote": search_tweaks_field_relevance_promote,
44
+ "search_tweaks_field_relevance_promote": promote_auth,
48
45
  }
49
46
 
50
47
 
51
- def search_tweaks_field_relevance_promote(context, data_dict):
48
+ def promote_auth(context, data_dict):
52
49
  try:
53
50
  tk.check_access("package_update", context, data_dict)
54
51
  except tk.NotAuthorized:
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
- from typing import Any, Optional
2
+
3
+ from typing import Any
3
4
 
4
5
  from flask import Blueprint
5
6
  from flask.views import MethodView
@@ -10,9 +11,7 @@ import ckan.plugins.toolkit as tk
10
11
  CONFIG_ENABLE_PROMOTION_ROUTE = (
11
12
  "ckanext.search_tweaks.field_relevance.blueprint.promotion.enabled"
12
13
  )
13
- CONFIG_PROMOTION_PATH = (
14
- "ckanext.search_tweaks.field_relevance.blueprint.promotion.path"
15
- )
14
+ CONFIG_PROMOTION_PATH = "ckanext.search_tweaks.field_relevance.blueprint.promotion.path"
16
15
  CONFIG_MAX_PROMOTION = (
17
16
  "ckanext.search_tweaks.field_relevance.blueprint.promotion.max_value"
18
17
  )
@@ -30,14 +29,10 @@ field_relevance = Blueprint("search_tweaks_field_relevance", __name__)
30
29
 
31
30
  def get_blueprints():
32
31
  if tk.asbool(
33
- tk.config.get(
34
- CONFIG_ENABLE_PROMOTION_ROUTE, DEFAULT_ENABLE_PROMOTION_ROUTE
35
- )
32
+ tk.config.get(CONFIG_ENABLE_PROMOTION_ROUTE, DEFAULT_ENABLE_PROMOTION_ROUTE),
36
33
  ):
37
34
  path = tk.config.get(CONFIG_PROMOTION_PATH, DEFAULT_PROMOTION_PATH)
38
- field_relevance.add_url_rule(
39
- path, view_func=PromoteView.as_view("promote")
40
- )
35
+ field_relevance.add_url_rule(path, view_func=PromoteView.as_view("promote"))
41
36
 
42
37
  return [field_relevance]
43
38
 
@@ -57,9 +52,9 @@ class PromoteView(MethodView):
57
52
  tk.get_validator("convert_int"),
58
53
  tk.get_validator("natural_number_validator"),
59
54
  tk.get_validator("limit_to_configured_maximum")(
60
- CONFIG_MAX_PROMOTION, DEFAULT_MAX_PROMOTION
55
+ CONFIG_MAX_PROMOTION, DEFAULT_MAX_PROMOTION,
61
56
  ),
62
- ]
57
+ ],
63
58
  }
64
59
 
65
60
  data, errors = tk.navl_validate(
@@ -72,7 +67,7 @@ class PromoteView(MethodView):
72
67
  return self.get(id, data, errors)
73
68
  try:
74
69
  pkg_dict = tk.get_action("package_patch")(
75
- {}, {"id": id, field: data[field]}
70
+ {}, {"id": id, field: data[field]},
76
71
  )
77
72
  except tk.ValidationError as e:
78
73
  for k, v in e.error_summary.items():
@@ -84,8 +79,8 @@ class PromoteView(MethodView):
84
79
  def get(
85
80
  self,
86
81
  id,
87
- data: Optional[dict[str, Any]] = None,
88
- errors: Optional[dict[str, Any]] = None,
82
+ data: dict[str, Any] | None = None,
83
+ errors: dict[str, Any] | None = None,
89
84
  ):
90
85
  self._check_access(id)
91
86
  field = tk.config.get(CONFIG_PROMOTION_FIELD, DEFAULT_PROMOTION_FIELD)
@@ -95,11 +90,9 @@ class PromoteView(MethodView):
95
90
  "errors": errors or {},
96
91
  "data": data or pkg_dict,
97
92
  "max_promotion": tk.asint(
98
- tk.config.get(CONFIG_MAX_PROMOTION, DEFAULT_MAX_PROMOTION)
93
+ tk.config.get(CONFIG_MAX_PROMOTION, DEFAULT_MAX_PROMOTION),
99
94
  ),
100
95
  "field_name": field,
101
96
  }
102
97
 
103
- return tk.render(
104
- "search_tweaks/field_relevance/promote.html", extra_vars
105
- )
98
+ return tk.render("search_tweaks/field_relevance/promote.html", extra_vars)
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from ckan.plugins.interfaces import Interface
6
+
7
+
8
+ class ISearchTweaks(Interface):
9
+ def get_search_boost_fn(self, search_params: dict[str, Any]) -> str | None:
10
+ """Return Solr's boost function applicable to the current search.
11
+
12
+ Note: it will be applied as `boost` when
13
+ `ckanext.search_tweaks.common.prefer_boost` enabled and as `bf`
14
+ otherwise.
15
+
16
+ """
17
+ return None
18
+
19
+ def get_extra_qf(self, search_params: dict[str, Any]) -> str | None:
20
+ """Return an additional fragment of the Solr's qf.
21
+
22
+ This fragment will be appended to the current qf
23
+ """
24
+ return None
25
+
26
+
27
+
28
+ class IQueryPopularity(Interface):
29
+ def skip_query_popularity(self, params: dict[str, Any]) -> bool:
30
+ """Do not index search query.
31
+ """
32
+ return False
@@ -1,56 +1,37 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
+ from typing import Any
4
5
 
5
- from typing import Any, Dict
6
6
  import ckan.plugins as plugins
7
7
  import ckan.plugins.toolkit as tk
8
- from ckan.lib.search.query import QUERY_FIELDS
9
-
10
- from . import cli, boost_preffered, feature_disabled
8
+ from . import feature_disabled, config
11
9
  from .interfaces import ISearchTweaks
12
10
 
13
11
  log = logging.getLogger(__name__)
14
-
15
- SearchParams = Dict[str, Any]
16
-
17
- CONFIG_QF = "ckanext.search_tweaks.common.qf"
18
- CONFIG_FUZZY = "ckanext.search_tweaks.common.fuzzy_search.enabled"
19
- CONFIG_FUZZY_DISTANCE = "ckanext.search_tweaks.common.fuzzy_search.distance"
20
- CONFIG_MM = "ckanext.search_tweaks.common.mm"
21
- CONFIG_FUZZY_KEEP_ORIGINAL = (
22
- "ckanext.search_tweaks.common.fuzzy_search.keep_original"
23
- )
24
-
25
- DEFAULT_QF = QUERY_FIELDS
26
- DEFAULT_FUZZY = False
27
- DEFAULT_FUZZY_DISTANCE = 1
28
- DEFAULT_MM = "1"
29
- DEFAULT_FUZZY_KEEP_ORIGINAL = True
12
+ CONFIG_PREFER_BOOST = "ckanext.search_tweaks.common.prefer_boost"
13
+ DEFAULT_PREFER_BOOST = True
30
14
 
31
15
 
16
+ @tk.blanket.cli
17
+ @tk.blanket.config_declarations
32
18
  class SearchTweaksPlugin(plugins.SingletonPlugin):
33
- plugins.implements(plugins.IClick)
34
19
  plugins.implements(plugins.IPackageController, inherit=True)
35
20
 
36
- # IClick
37
-
38
- def get_commands(self):
39
- return cli.get_commands()
40
-
41
21
  # IPackageController
42
22
 
43
- def before_dataset_search(self, search_params: SearchParams):
23
+ def before_dataset_search(self, search_params: dict[str, Any]):
44
24
  if feature_disabled("everything", search_params):
45
25
  return search_params
46
26
 
47
- search_params.setdefault("mm", tk.config.get(CONFIG_MM, DEFAULT_MM))
27
+ search_params.setdefault("mm", config.mm())
48
28
 
49
29
  if "defType" not in search_params:
50
30
  search_params["defType"] = "edismax"
51
31
 
52
- if boost_preffered() and search_params["defType"] == "edismax":
32
+ if config.prefer_boost() and search_params["defType"] == "edismax":
53
33
  _set_boost(search_params)
34
+
54
35
  else:
55
36
  _set_bf(search_params)
56
37
 
@@ -60,7 +41,7 @@ class SearchTweaksPlugin(plugins.SingletonPlugin):
60
41
  return search_params
61
42
 
62
43
 
63
- def _set_boost(search_params: SearchParams) -> None:
44
+ def _set_boost(search_params: dict[str, Any]) -> None:
64
45
  boost: list[str] = search_params.setdefault("boost", [])
65
46
  for plugin in plugins.PluginImplementations(ISearchTweaks):
66
47
  extra = plugin.get_search_boost_fn(search_params)
@@ -69,7 +50,7 @@ def _set_boost(search_params: SearchParams) -> None:
69
50
  boost.append(extra)
70
51
 
71
52
 
72
- def _set_bf(search_params: SearchParams) -> None:
53
+ def _set_bf(search_params: dict[str, Any]) -> None:
73
54
  default_bf: str = search_params.get("bf") or "0"
74
55
  search_params.setdefault("bf", default_bf)
75
56
  for plugin in plugins.PluginImplementations(ISearchTweaks):
@@ -79,13 +60,11 @@ def _set_bf(search_params: SearchParams) -> None:
79
60
  search_params["bf"] = f"sum({search_params['bf']},{extra_bf})"
80
61
 
81
62
 
82
- def _set_qf(search_params: SearchParams) -> None:
63
+ def _set_qf(search_params: dict[str, Any]) -> None:
83
64
  if feature_disabled("qf", search_params):
84
65
  return
85
66
 
86
- default_qf: str = search_params.get("qf") or tk.config.get(
87
- CONFIG_QF, DEFAULT_QF
88
- )
67
+ default_qf: str = search_params.get("qf") or config.qf()
89
68
  search_params.setdefault("qf", default_qf)
90
69
  for plugin in plugins.PluginImplementations(ISearchTweaks):
91
70
  extra_qf = plugin.get_extra_qf(search_params)
@@ -94,8 +73,8 @@ def _set_qf(search_params: SearchParams) -> None:
94
73
  search_params["qf"] += " " + extra_qf
95
74
 
96
75
 
97
- def _set_fuzzy(search_params: SearchParams) -> None:
98
- if not tk.asbool(tk.config.get(CONFIG_FUZZY, DEFAULT_FUZZY)):
76
+ def _set_fuzzy(search_params: dict[str, Any]) -> None:
77
+ if not config.fuzzy():
99
78
  return
100
79
 
101
80
  if feature_disabled("fuzzy", search_params):
@@ -118,20 +97,16 @@ def _set_fuzzy(search_params: SearchParams) -> None:
118
97
  if s.isalpha() and s not in ("AND", "OR", "TO")
119
98
  else s,
120
99
  q.split(),
121
- )
100
+ ),
122
101
  )
123
- if tk.asbool(
124
- tk.config.get(CONFIG_FUZZY_KEEP_ORIGINAL, DEFAULT_FUZZY_KEEP_ORIGINAL)
125
- ):
102
+ if config.fuzzy_with_original():
126
103
  search_params["q"] = f"({fuzzy_q}) OR ({q})"
127
104
  else:
128
105
  search_params["q"] = fuzzy_q
129
106
 
130
107
 
131
108
  def _get_fuzzy_distance() -> int:
132
- distance = tk.asint(
133
- tk.config.get(CONFIG_FUZZY_DISTANCE, DEFAULT_FUZZY_DISTANCE)
134
- )
109
+ distance = config.fuzzy_distance()
135
110
  if distance < 0:
136
111
  log.warning("Cannot use negative fuzzy distance: %s.", distance)
137
112
  distance = 0
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+ import ckan.plugins.toolkit as tk
3
+
4
+
5
+ def skip_irrefutable() -> bool:
6
+ return tk.config["ckanext.search_tweaks.query_popularity.skip_irrefutable_search"]
7
+
8
+
9
+ def ignored_symbols() -> set[str]:
10
+ return set(tk.config["ckanext.search_tweaks.query_popularity.ignored_symbols"])
11
+
12
+
13
+ def ignored_terms() -> list[str]:
14
+ return tk.config["ckanext.search_tweaks.query_popularity.ignored_terms"]
15
+
16
+
17
+ def throttle() -> int:
18
+ return tk.config["ckanext.search_tweaks.query_popularity.query_throttle"]
19
+
20
+
21
+ def max_age() -> int:
22
+ return tk.config["ckanext.search_tweaks.query_popularity.max_age"]
23
+
24
+
25
+ def obsoletion_period() -> int:
26
+ return tk.config["ckanext.search_tweaks.query_popularity.obsoletion_period"]
27
+
28
+
29
+ def tracked_endpoints() -> list[str]:
30
+ return tk.config["ckanext.search_tweaks.query_popularity.tracked_endpoints"]
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+ from typing import Any
3
+ from ckan import types
4
+ import ckan.plugins.toolkit as tk
5
+
6
+ from ckanext.search_tweaks.query_popularity.score import Score
7
+
8
+
9
+ @tk.side_effect_free
10
+ def search_tweaks_query_popularity_list(
11
+ context: types.Context, data_dict: dict[str, Any]
12
+ ) -> list[dict[str, Any]]:
13
+ score = Score()
14
+
15
+ if tk.asbool(data_dict.get("refresh")):
16
+ score.refresh()
17
+
18
+ limit = tk.asint(data_dict.get("limit", 10))
19
+
20
+ return list(score.stats(limit))
21
+
22
+
23
+ @tk.side_effect_free
24
+ def search_tweaks_query_popularity_export(
25
+ context: types.Context, data_dict: dict[str, Any]
26
+ ) -> dict[str, Any]:
27
+ score = Score()
28
+
29
+ results = score.export()
30
+ return {"results": results, "count": len(results)}
31
+
32
+
33
+ @tk.side_effect_free
34
+ def search_tweaks_query_popularity_ignore(
35
+ context: types.Context, data_dict: dict[str, Any]
36
+ ):
37
+ q = tk.get_or_bust(data_dict, "q")
38
+ score = Score()
39
+ result = score.ignore(q)
40
+ if tk.asbool(data_dict.get("remove")):
41
+ score.drop(q)
42
+
43
+ return result
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+ from typing import Any
3
+ from ckan import types
4
+
5
+ from ckan.authz import is_authorized
6
+
7
+
8
+ def search_tweaks_query_popularity_list(
9
+ context: types.Context, data_dict: dict[str, Any]
10
+ ) -> types.AuthResult:
11
+ return is_authorized("sysadmin", context, data_dict)
12
+
13
+
14
+ def search_tweaks_query_popularity_export(
15
+ context: types.Context, data_dict: dict[str, Any]
16
+ ) -> types.AuthResult:
17
+ return is_authorized("sysadmin", context, data_dict)
18
+
19
+
20
+ def search_tweaks_query_popularity_ignore(
21
+ context: types.Context, data_dict: dict[str, Any]
22
+ ) -> types.AuthResult:
23
+ return is_authorized("sysadmin", context, data_dict)
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+ from typing import Any
3
+ import ckan.plugins as p
4
+ import ckan.plugins.toolkit as tk
5
+ from ckanext.search_tweaks.interfaces import IQueryPopularity
6
+ from . import config, score
7
+
8
+
9
+ @tk.blanket.actions
10
+ @tk.blanket.auth_functions
11
+ @tk.blanket.config_declarations
12
+ class QueryPopularityPlugin(p.SingletonPlugin):
13
+ p.implements(p.IConfigurable)
14
+ p.implements(p.IPackageController, inherit=True)
15
+ p.implements(IQueryPopularity, inherit=True)
16
+
17
+ def after_dataset_search(self, results: dict[str, Any], params: dict[str, Any]):
18
+ bp, view = tk.get_endpoint()
19
+ if bp and view and f"{bp}.{view}" in config.tracked_endpoints():
20
+ if not any(
21
+ plugin.skip_query_popularity(params)
22
+ for plugin in p.PluginImplementations(IQueryPopularity)
23
+ ):
24
+ self.score.save(params["q"])
25
+
26
+ return results
27
+
28
+ def configure(self, config: Any):
29
+ self.score = score.Score()
30
+
31
+ def skip_query_popularity(self, params: dict[str, Any]) -> bool:
32
+ q = params["q"]
33
+
34
+ if q == "*:*":
35
+ return config.skip_irrefutable()
36
+
37
+ symbols = config.ignored_symbols()
38
+ if symbols and set(q) & symbols:
39
+ return True
40
+
41
+ terms = config.ignored_terms()
42
+
43
+ for term in terms:
44
+ if term in q:
45
+ return True
46
+
47
+ return False