Coverage for ckanext/udc/search/logic/actions.py: 25%
89 statements
« prev ^ index » next coverage.py v7.7.1, created at 2026-01-19 23:48 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2026-01-19 23:48 +0000
1from __future__ import annotations
2from typing import Any, Callable, Collection, KeysView, Optional, Union, cast
3from ckanext.udc.search.logic.utils import profile_func, cache_for
4from ckan import model, authz, logic
5from ckan.common import _, config, current_user
6import ckan.plugins as plugins
7import ckan.lib.helpers as h
8from ckan.plugins.toolkit import side_effect_free
10import logging
12log = logging.getLogger(__name__)
15# @profile_func
16@side_effect_free
17def filter_facets_get(context, data_dict):
18 return _filter_facets_get(data_dict)
20from collections import OrderedDict
21from typing import Any
22import ckan.plugins as plugins
23import ckan.plugins.toolkit as tk
24import ckan.logic as logic
25import ckan.lib.helpers as h
27from ckanext.udc.solr.config import get_current_lang
30def _facet_cache_key(data_dict=None, *_, **kwargs):
31 if isinstance(data_dict, dict):
32 lang = data_dict.get("lang")
33 else:
34 lang = None
36 if not lang and "lang" in kwargs:
37 lang = kwargs["lang"]
39 if not lang and isinstance(kwargs.get("data_dict"), dict):
40 lang = kwargs["data_dict"].get("lang")
42 if not lang:
43 lang = get_current_lang()
45 return lang or "__default__"
48@cache_for(60, key_func=_facet_cache_key)
49def _filter_facets_get(data_dict) -> dict[str, Any]:
50 """
51 data_dict only needs to contain "lang" (optional).
53 Multilingual facets with stable outward keys:
55 'tags' -> Solr 'tags_<lang>_f'
56 '<text_field>' -> Solr '<text_field>_<lang>_f'
57 'extras_<name>' -> Solr 'extras_<name>' (non-text stays as-is)
59 The Solr response is renamed back to the stable keys so the UI
60 and your existing code keep working unchanged.
61 """
62 lang = data_dict.get("lang") or get_current_lang()
64 # 1) Gather facet keys (prefer plugin-provided; then CKAN defaults)
65 ordered_plugin_keys: list[str] = []
66 for p in plugins.PluginImplementations(plugins.IFacets):
67 try:
68 provided = p.dataset_facets(OrderedDict(), "catalogue") or {}
69 for k in provided.keys():
70 if k not in ordered_plugin_keys:
71 ordered_plugin_keys.append(k)
72 except Exception:
73 continue
75 facet_keys: list[str] = []
76 for k in ordered_plugin_keys + list(h.facets()):
77 if k not in facet_keys:
78 facet_keys.append(k)
80 # 2) Build alias -> Solr mapping
81 try:
82 udc = plugins.get_plugin("udc")
83 text_fields = set(udc.text_fields or [])
84 dropdown_options = udc.dropdown_options or {}
85 except Exception:
86 text_fields = set()
87 dropdown_options = {}
89 alias_to_solr: OrderedDict[str, str] = OrderedDict()
90 for key in facet_keys:
91 if key == "tags":
92 alias_to_solr[key] = f"tags_{lang}_f"
93 # version relationship helper facets: use *_title_url for display
94 # but map outward stable keys to URL-only Solr fields
95 elif key == "version_dataset":
96 alias_to_solr[key] = "version_dataset_url"
97 elif key == "dataset_versions":
98 alias_to_solr[key] = "dataset_versions_url"
99 elif key.startswith("extras_"):
100 # Non-text maturity fields (date/number/select) facet via extras_*
101 alias_to_solr[key] = key
102 elif key in text_fields:
103 # Text maturity fields use language-specific facet fields
104 alias_to_solr[key] = f"{key}_{lang}_f"
105 else:
106 # Any other core facet stays as-is
107 alias_to_solr[key] = key
109 facet_fields_solr = list(dict.fromkeys(alias_to_solr.values())) # de-dupe preserve order
111 # 3) Query Solr
112 try:
113 default_limit = int(tk.config.get("search.facets.default", 10))
114 except Exception:
115 default_limit = 10
117 data_dict: dict[str, Any] = {
118 "q": "*:*",
119 "facet.limit": -1,
120 "facet.field": facet_fields_solr,
121 "rows": default_limit,
122 "start": 0,
123 "fq": 'capacity:"public"',
124 }
125 query = logic.get_action("package_search")({}, data_dict)
126 raw_facets: dict[str, Any] = query.get("search_facets", {})
128 # 4) Rename Solr facet keys back to stable outward keys
129 facets: dict[str, Any] = {}
130 for alias, solr_name in alias_to_solr.items():
131 if solr_name in raw_facets:
132 facets[alias] = raw_facets[solr_name]
134 # 5) Localize dropdown option labels for extras_* facets
135 for stable_key, payload in facets.items():
136 # For extras_*, strip prefix to lookup configured option labels
137 base = stable_key[7:] if stable_key.startswith("extras_") else stable_key
138 options_map = dropdown_options.get(base)
139 if not options_map:
140 continue
141 for item in payload.get("items", []):
142 label = options_map.get(item.get("name"))
143 if label:
144 item["display_name"] = label
146 return facets