Coverage for ckanext/udc/plugin.py: 80%
278 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
3import sys
4import json
5import traceback
6import chalk
7from collections import OrderedDict
8from typing import (
9 Any,
10 Callable,
11 Collection,
12 KeysView,
13 Optional,
14 Union,
15 cast,
16 Iterable,
17 List,
18)
19from functools import partial
21from ckanext.udc.search.logic.actions import filter_facets_get
22from ckan.types import Schema, Context, CKANApp, Response, SignalMapping
23import ckan
24import ckan.plugins as plugins
25import ckan.logic as logic
26import ckan.model as model
27import ckan.plugins.toolkit as tk
28import ckan.lib.base as base
29from ckan.plugins.toolkit import chained_action, side_effect_free
30import ckan.lib.helpers as h
31from ckan.lib.helpers import Page
32from ckan.common import asbool, current_user, CKANConfig, request, g, config, _
33from ckan.lib.search import SearchQueryError, SearchError
34from ckan.lib.plugins import DefaultTranslation
36from flask import Blueprint
38import logging
39from ckanext.udc.cli import udc as cli_udc
40from ckanext.udc.validator import udc_config_validator
41from ckanext.udc.helpers import (
42 config_option_update,
43 get_default_facet_titles,
44 process_facets_fields,
45 humanize_entity_type,
46 get_maturity_percentages,
47 package_update,
48 package_delete,
49 get_system_info,
50 udc_json_attr,
51 render_markdown,
52)
53from ckanext.udc.solr.config import pick_locale, pick_locale_with_fallback, get_udc_langs, get_current_lang
54from ckanext.udc.graph.sparql_client import SparqlClient
55from ckanext.udc.graph.preload import preload_ontologies
56from ckanext.udc.graph.logic import get_catalogue_graph
57from babel import Locale
59from ckanext.udc.licenses.logic.action import (
60 license_create,
61 license_delete,
62 licenses_get,
63 license_update,
64 init_licenses,
65 test_long_task,
66)
67from ckanext.udc.licenses.utils import license_options_details
68from ckanext.udc.file_format.logic import (
69 file_format_create,
70 file_format_delete,
71 file_formats_get,
72)
73from ckanext.udc.desc.actions import (
74 summary_generate,
75 update_summary,
76 default_ai_summary_config,
77)
78from ckanext.udc.desc.utils import init_plugin as init_udc_desc
79from ckanext.udc.error_handler import override_error_handler
80from ckanext.udc.system.actions import reload_supervisord, get_system_stats
81from ckanext.udc.version.actions import udc_version_meta
82from ckanext.udc.solr.solr import update_solr_maturity_model_fields
83from ckanext.udc.solr.index import before_dataset_index as _before_dataset_index
85from ckanext.udc.search.logic.actions import filter_facets_get
86from ckanext.udc.user.actions import (
87 deleted_users_list,
88 purge_deleted_users,
89 udc_user_list,
90 udc_user_reset_password,
91 udc_user_delete,
92)
93from ckanext.udc.user import auth as user_auth
94from .i18n import (
95 udc_lang_object,
96 udc_json_dump,
97 udc_json_load,
98 udc_core_translated_to_extras,
99 udc_set_core_from_translated,
100 udc_lang_string_list,
101 udc_set_core_tags_from_translated,
102 udc_fill_tags_translated_from_core,
103 udc_seed_translated_from_core,
104 udc_fill_translated_from_core_on_show,
105)
108"""
109See https://docs.ckan.org/en/latest/theming/templates.html
110See https://docs.ckan.org/en/latest/extensions/adding-custom-fields.html
111See https://docs.ckan.org/en/2.10/extensions/remote-config-update.html
112See https://docs.ckan.org/en/2.10/extensions/custom-config-settings.html?highlight=config%20declaration
113See https://docs.ckan.org/en/2.10/theming/webassets.html
114"""
115log = logging.getLogger(__name__)
117# Add UDC CLI
118# We can then `ckan -c /etc/ckan/default/ckan.ini udc move-to-catalogues` to run the migration script
119if hasattr(ckan, "cli") and hasattr(ckan.cli, "cli"):
120 ckan.cli.cli.ckan.add_command(cli_udc.udc)
123@tk.blanket.blueprints
124class UdcPlugin(plugins.SingletonPlugin, tk.DefaultDatasetForm, DefaultTranslation):
125 plugins.implements(plugins.IConfigurer)
126 plugins.implements(plugins.IConfigurable)
127 plugins.implements(plugins.IDatasetForm)
128 plugins.implements(plugins.ITemplateHelpers)
129 plugins.implements(plugins.IActions)
130 plugins.implements(plugins.IAuthFunctions)
131 plugins.implements(plugins.IFacets)
132 plugins.implements(plugins.IPackageController)
133 plugins.implements(plugins.IMiddleware)
134 plugins.implements(plugins.IBlueprint)
135 plugins.implements(plugins.IValidators)
136 plugins.implements(plugins.ITranslation)
138 disable_graphdb = False
139 maturity_model = []
140 mappings = {}
141 preload_ontologies = {}
142 all_fields: List[str] = [] # This does not contain core CKAN fields
143 facet_titles = {}
144 facet_titles_raw = {} # multilingual titles, not picked to current locale
145 text_fields: List[str] = []
146 date_fields: List[str] = []
147 multiple_select_fields: List[str] = []
148 dropdown_options: dict[str, dict[str, str]] = {}
150 def update_config(self, config_):
151 tk.add_template_directory(config_, "templates")
152 tk.add_public_directory(config_, "public")
153 tk.add_resource("assets", "udc")
155 def _cli_configure(self):
156 """Partial load for CLI"""
157 self.disable_graphdb = True
158 existing_config = ckan.model.system_info.get_system_info("ckanext.udc.config")
159 if existing_config:
160 try:
161 # Call our plugin to update the config
162 self.reload_config(json.loads(existing_config))
163 except:
164 log.error
166 # Load custom licenses
167 init_licenses()
169 def configure(self, config: CKANConfig):
170 log.info(sys.argv)
171 if not (
172 "run" in sys.argv
173 or "uwsgi" in sys.argv
174 or ("jobs" in sys.argv and "worker" in sys.argv)
175 or ("search-index" in sys.argv)
176 or ("translation" in sys.argv)
177 or ("asset" in sys.argv)
178 ):
179 log.info("Skipping UDC Plugin Configuration")
180 # Do not load the plugin if we are running the CLI
181 self._cli_configure()
182 return
183 existing_config = ckan.model.system_info.get_system_info("ckanext.udc.config")
184 # print(existing_config)
186 # Load sparql client
187 endpoint = tk.config.get("udc.sparql.endpoint")
188 username = tk.config.get("udc.sparql.username") or None
189 password = tk.config.get("udc.sparql.password") or None
191 if endpoint is None:
192 self.disable_graphdb = True
193 log.info("No GraphDB Endpoint is provided.")
195 else:
196 self.sparql_client = SparqlClient(
197 endpoint, username=username, password=password
198 )
199 if self.sparql_client.test_connecetion():
200 log.info("GraphDB connected: " + endpoint)
201 else:
202 log.error("UDC cannot connect to the GraphDB")
203 self.disable_graphdb = True
205 # Load config
206 if existing_config:
207 try:
208 # Call our plugin to update the config
209 self.reload_config(json.loads(existing_config))
210 except:
211 log.error
213 # Load custom licenses
214 init_licenses()
216 # Init chatgpt summary plugin
217 init_udc_desc()
219 log.info("UDC Plugin Loaded!")
221 def reload_config(self, config: list):
222 try:
223 # log.info("tring to load udc config:")
224 # log.info(config)
225 all_fields = []
226 self.facet_titles.clear()
227 self.facet_titles_raw.clear()
228 self.text_fields.clear()
229 self.date_fields.clear()
230 self.multiple_select_fields.clear()
231 self.dropdown_options.clear()
232 for level in config["maturity_model"]:
233 for field in level["fields"]:
234 if field.get("name"):
235 all_fields.append(field["name"])
236 type = field.get("type")
237 if field.get("name") and (
238 type == ""
239 or type is None
240 or type == "text"
241 or type == "single_select"
242 or type == "multiple_select"
243 or type == "number"
244 or type == "date"
245 ):
246 self.facet_titles[field["name"]] = tk._(pick_locale(field["label"], 'en'))
247 self.facet_titles_raw[field["name"]] = field["label"]
248 if field.get("name") and (type == "text" or type is None):
249 self.text_fields.append(field["name"])
250 if field.get("type") == "date":
251 self.date_fields.append(field["name"])
252 if field.get("type") == "multiple_select":
253 self.multiple_select_fields.append(field["name"])
255 # Preload ontologies
256 if not self.disable_graphdb:
257 endpoint = tk.config.get("udc.sparql.endpoint")
258 username = tk.config.get("udc.sparql.username") or None
259 password = tk.config.get("udc.sparql.password") or None
260 # This will preload ontologies and
261 # populate options to the fields that uses 'optionsFromQuery'
262 preload_ontologies(
263 config, endpoint, username, password, self.sparql_client
264 )
266 # Store dropdown options
267 for level in config["maturity_model"]:
268 for field in level["fields"]:
269 if (
270 field.get("type") == "multiple_select"
271 or field.get("type") == "single_select"
272 ):
273 options = self.dropdown_options[field["name"]] = {}
274 for option in field["options"]:
275 options[option["value"]] = tk._(option["text"])
276 log.info(
277 f"Dropdown options for field {field['name']} loaded: {len(options.keys())}"
278 )
280 # Update solr index
281 update_solr_maturity_model_fields(config["maturity_model"])
283 # Do not mutate the vars
284 self.all_fields.clear()
285 self.all_fields.extend(all_fields)
286 self.maturity_model.clear()
287 self.maturity_model.extend(config["maturity_model"])
288 self.mappings.clear()
289 self.mappings.update(config["mappings"])
290 self.preload_ontologies.clear()
291 self.preload_ontologies.update(config["preload_ontologies"])
293 except Exception as e:
294 log.error("UDC Plugin Error:")
295 traceback.print_exc()
297 def _modify_package_schema(self, schema: Schema) -> Schema:
298 """
299 Wire CUDC custom fields into CKAN:
300 - For type="text" custom fields: accept multilingual {lang: value} and store as JSON string in extras.
301 - For other types (date/number/select/etc.): keep original convert_to_extras for now.
302 """
303 # our custom fields
304 for field in self.all_fields:
305 if field in self.text_fields:
306 # Multilingual text: validate object -> dump JSON -> extras
307 schema.update(
308 {
309 field: [
310 tk.get_validator("ignore_missing"),
311 udc_json_load,
312 udc_lang_object, # expects {"en": "...", "fr": "..."} (fr optional)
313 udc_json_dump, # store as JSON string in extras
314 tk.get_converter("convert_to_extras"),
315 ],
316 }
317 )
318 else:
319 # Non-text
320 schema.update(
321 {
322 field: [
323 tk.get_validator("ignore_missing"),
324 tk.get_converter("convert_to_extras"),
325 ],
326 }
327 )
329 # ---- Multilingual core fields (replicate fluent_core_translated) ----
330 # title_translated / notes_translated store {lang: value} as JSON in extras,
331 # and keep `title` / `notes` as the default-locale strings.
333 schema.update(
334 {
335 "title_translated": [
336 tk.get_validator("ignore_missing"),
337 udc_json_load,
338 udc_seed_translated_from_core(
339 "title"
340 ), # seed from core title if missing
341 udc_lang_object,
342 udc_core_translated_to_extras("title"),
343 udc_json_dump,
344 tk.get_converter("convert_to_extras"),
345 ],
346 "notes_translated": [
347 tk.get_validator("ignore_missing"),
348 udc_json_load,
349 udc_seed_translated_from_core(
350 "notes"
351 ), # seed from core notes if missing
352 udc_lang_object,
353 udc_core_translated_to_extras("notes"),
354 udc_json_dump,
355 tk.get_converter("convert_to_extras"),
356 ],
357 # Accept: {"en": ["roads","transport"], "fr": ["routes","transport"]}
358 "tags_translated": [
359 tk.get_validator("ignore_missing"),
360 udc_json_load,
361 udc_lang_string_list,
362 udc_set_core_tags_from_translated, # set core 'tags' from tags_translated[default_lang]
363 udc_json_dump,
364 tk.get_converter("convert_to_extras"),
365 ],
366 "udc_import_extras": [
367 tk.get_validator("ignore_missing"),
368 udc_json_dump,
369 tk.get_converter("convert_to_extras"),
370 ],
371 }
372 )
374 schema.update(
375 {
376 # For import from other portals plugin
377 "cudc_import_config_id": [
378 tk.get_validator("ignore_missing"),
379 tk.get_converter("convert_to_extras"),
380 ],
381 # Keep track of the remote ID from the source portal
382 "cudc_import_remote_id": [
383 tk.get_validator("ignore_missing"),
384 tk.get_converter("convert_to_extras"),
385 ],
386 "is_unified": [
387 tk.get_validator("ignore_missing"),
388 tk.get_validator("boolean_validator"),
389 tk.get_converter("convert_to_extras"),
390 ],
391 "source_last_updated": [
392 tk.get_validator("ignore_missing"),
393 tk.get_converter("convert_to_extras"),
394 ],
395 # ---- chatgpt summary ------
396 "summary": [
397 tk.get_validator("ignore_missing"),
398 tk.get_converter("convert_to_extras"),
399 ],
400 }
401 )
402 return schema
404 def create_package_schema(self) -> Schema:
405 # let's grab the default schema in our plugin
406 schema: Schema = super(UdcPlugin, self).create_package_schema()
407 return self._modify_package_schema(schema)
409 def update_package_schema(self) -> Schema:
410 schema: Schema = super(UdcPlugin, self).update_package_schema()
411 # our custom field
412 return self._modify_package_schema(schema)
414 def show_package_schema(self) -> Schema:
415 schema: Schema = super(UdcPlugin, self).show_package_schema()
416 for field in self.all_fields:
417 if field in self.text_fields:
418 # Multilingual text: load JSON from extras
419 schema.update(
420 {
421 field: [
422 tk.get_converter("convert_from_extras"),
423 udc_json_load, # load JSON string from extras
424 tk.get_validator("ignore_missing"),
425 ],
426 }
427 )
428 else:
429 # Non-text: load directly from extras
430 schema.update(
431 {
432 field: [
433 tk.get_converter("convert_from_extras"),
434 tk.get_validator("ignore_missing"),
435 ],
436 }
437 )
439 # bring translated JSON back and ensure core fields are present
440 schema.update(
441 {
442 "title_translated": [
443 tk.get_converter("convert_from_extras"),
444 udc_json_load,
445 udc_fill_translated_from_core_on_show("title"),
446 tk.get_validator("ignore_missing"),
447 ],
448 "notes_translated": [
449 tk.get_converter("convert_from_extras"),
450 udc_json_load,
451 udc_fill_translated_from_core_on_show("notes"),
452 tk.get_validator("ignore_missing"),
453 ],
454 # Return tags_translated as a dict; if missing, seed from core tags
455 "tags_translated": [
456 tk.get_converter("convert_from_extras"),
457 udc_json_load,
458 udc_fill_tags_translated_from_core,
459 tk.get_validator("ignore_missing"),
460 ],
461 }
462 )
463 # ensure title/notes fallback to default locale from *_translated on show
464 schema.setdefault("title", []).append(udc_set_core_from_translated("title"))
465 schema.setdefault("notes", []).append(udc_set_core_from_translated("notes"))
467 schema.update(
468 {
469 # For import from other portals plugin
470 "cudc_import_config_id": [
471 tk.get_converter("convert_from_extras"),
472 tk.get_validator("ignore_missing"),
473 ],
474 # Keep track of the remote ID from the source portal
475 "cudc_import_remote_id": [
476 tk.get_converter("convert_from_extras"),
477 tk.get_validator("ignore_missing"),
478 ],
479 # For Unified Package:
480 # If the package is unified, it uses 'unified_has_versions' to link other packages.
481 # For every other packages that is not unified, use 'potential_duplicates' to link other duplicated packages.
482 "is_unified": [
483 tk.get_converter("convert_from_extras"),
484 tk.get_validator("ignore_missing"),
485 ],
486 "source_last_updated": [
487 tk.get_converter("convert_from_extras"),
488 tk.get_validator("ignore_missing"),
489 ],
490 # ---- chatgpt summary ------
491 "summary": [
492 tk.get_converter("convert_from_extras"),
493 tk.get_validator("ignore_missing"),
494 ],
495 # Version relations: decode JSON stored in extras into
496 # native dict/list structures for templates and Solr indexer.
497 "version_dataset": [
498 tk.get_converter("convert_from_extras"),
499 udc_json_load,
500 tk.get_validator("ignore_missing"),
501 ],
502 "dataset_versions": [
503 tk.get_converter("convert_from_extras"),
504 udc_json_load,
505 tk.get_validator("ignore_missing"),
506 ],
507 "udc_import_extras": [
508 tk.get_converter("convert_from_extras"),
509 udc_json_load,
510 tk.get_validator("ignore_missing"),
511 ],
512 }
513 )
515 return schema
517 def get_helpers(self):
518 langs = get_udc_langs()
519 language_labels = {}
521 for code in langs:
522 label = self._language_label(code, langs[0] if langs else "en")
523 language_labels[code] = label
525 return {
526 "render_markdown": render_markdown,
527 "config": self.maturity_model,
528 "maturity_model": self.maturity_model,
529 "facet_titles": self.facet_titles,
530 "facet_titles_raw": self.facet_titles_raw,
531 "maturity_model_text_fields": self.text_fields,
532 "udc_languages": langs,
533 "udc_default_lang": langs[0] if langs else "en",
534 "udc_language_labels": language_labels,
535 "get_default_facet_titles": get_default_facet_titles,
536 "process_facets_fields": process_facets_fields,
537 "humanize_entity_type": humanize_entity_type,
538 "get_maturity_percentages": get_maturity_percentages,
539 "get_system_info": get_system_info,
540 "udc_json_attr": udc_json_attr,
541 "license_options_details": license_options_details,
542 "pick_locale_with_fallback": pick_locale_with_fallback,
543 }
545 def _language_label(self, code: str, fallback: str) -> str:
546 """Return a human readable language name for templates."""
548 default_lang = (fallback or tk.config.get("ckan.locale_default", "en") or "en").replace("-", "_")
550 try:
551 display_locale = Locale.parse(default_lang)
552 target = Locale.parse(code.replace("-", "_"))
553 label = target.get_display_name(display_locale)
554 if isinstance(label, str) and label:
555 return label[0].upper() + label[1:]
556 except Exception:
557 pass
559 return code.upper()
561 def is_fallback(self):
562 # Return True to register this plugin as the default handler for
563 # package types not handled by any other IDatasetForm plugin.
564 return True
566 def package_types(self): # -> list[str]
567 # This plugin just registers itself as the 'catalogue'.
568 return ["dataset", "catalogue"]
570 def update_config_schema(self, schema: Schema):
572 ignore_missing = tk.get_validator("ignore_missing")
573 unicode_safe = tk.get_validator("unicode_safe")
574 json_object = tk.get_validator("json_object")
576 schema.update(
577 {
578 # This is a custom configuration option
579 "ckanext.udc.config": [
580 ignore_missing,
581 unicode_safe,
582 udc_config_validator,
583 ],
584 "ckanext.udc.group_side_panel_text": [ignore_missing, unicode_safe],
585 "ckanext.udc.organization_side_panel_text": [
586 ignore_missing,
587 unicode_safe,
588 ],
589 "ckanext.udc.dataset_side_panel_text": [ignore_missing, unicode_safe],
590 "ckanext.udc.catalogue_side_panel_text": [ignore_missing, unicode_safe],
591 # ---- chatgpt summary config ------
592 "ckanext.udc.desc.config": [ignore_missing, unicode_safe],
593 }
594 )
596 return schema
598 def get_actions(self):
599 """
600 Override CKAN's default actions.
601 """
602 return {
603 "config_option_update": config_option_update,
604 "package_update": package_update,
605 "package_delete": package_delete,
606 # Custom Licenses
607 "license_create": license_create,
608 "license_delete": license_delete,
609 "licenses_get": licenses_get,
610 "license_update": license_update,
611 "test_long_task": test_long_task,
612 # Custom file format
613 "file_format_create": file_format_create,
614 "file_format_delete": file_format_delete,
615 "file_formats_get": file_formats_get,
616 # Chatgpt summary actions
617 "summary_generate": summary_generate,
618 "update_summary": update_summary,
619 "default_ai_summary_config": default_ai_summary_config,
620 # System actions
621 "reload_supervisord": reload_supervisord,
622 "get_system_stats": get_system_stats,
623 # Version metadata helper
624 "udc_version_meta": udc_version_meta,
625 # "maturity_model_get": get_maturity_model,
626 # Filters
627 "filter_facets_get": filter_facets_get,
628 # User management
629 "deleted_users_list": deleted_users_list,
630 "purge_deleted_users": purge_deleted_users,
631 "udc_user_list": udc_user_list,
632 "udc_user_reset_password": udc_user_reset_password,
633 "udc_user_delete": udc_user_delete,
634 }
636 def get_auth_functions(self):
637 """
638 Register custom authorization functions.
639 """
640 return {
641 "deleted_users_list": user_auth.deleted_users_list,
642 "purge_deleted_users": user_auth.purge_deleted_users,
643 "udc_user_list": user_auth.udc_user_list,
644 "udc_user_reset_password": user_auth.udc_user_reset_password,
645 "udc_user_delete": user_auth.udc_user_delete,
646 }
648 def dataset_facets(self, facets_dict: OrderedDict[str, Any], package_type: str):
649 for name in self.facet_titles:
650 if name in self.text_fields:
651 # The text fields (solr type=text) can be used as facets
652 # We need to use the builtin facet name
653 # and not the extras_ prefix
654 facets_dict[name] = pick_locale(self.facet_titles_raw[name])
655 else:
656 # We need the extras_ prefix for our custom solr schema
657 facets_dict["extras_" + name] = pick_locale(self.facet_titles_raw[name])
659 return facets_dict
661 def group_facets(
662 self,
663 facets_dict: OrderedDict[str, Any],
664 group_type: str,
665 package_type: Optional[str],
666 ):
667 return facets_dict
669 def organization_facets(
670 self,
671 facets_dict: OrderedDict[str, Any],
672 organization_type: str,
673 package_type: Optional[str],
674 ):
675 return facets_dict
677 def read(self, entity: "model.Package") -> None:
678 print(chalk.red("create"))
679 pass
681 def create(self, entity: "model.Package") -> None:
682 pass
684 def edit(self, entity: "model.Package") -> None:
685 pass
687 def delete(self, entity: "model.Package") -> None:
688 pass
690 def after_dataset_create(self, context: Context, pkg_dict: dict[str, Any]) -> None:
691 pass
693 def after_dataset_update(self, context: Context, pkg_dict: dict[str, Any]) -> None:
694 pass
696 def after_dataset_delete(self, context: Context, pkg_dict: dict[str, Any]) -> None:
697 pass
699 def after_dataset_show(self, context: Context, pkg_dict: dict[str, Any]) -> None:
700 if context.get("for_update"):
701 # Avoid injecting related_packages and udc_import_extras during package_update/patch.
702 pkg_dict.pop("udc_import_extras", None)
703 return
704 # Add related packages
705 related_packages = []
707 if pkg_dict.get("is_unified"):
708 rel = (
709 model.meta.Session.query(model.PackageRelationship)
710 .filter(model.PackageRelationship.subject_package_id == pkg_dict["id"])
711 .all()
712 )
713 for r in rel:
714 related_package = model.Package.get(r.object_package_id)
715 related_packages.append(
716 {
717 "title": related_package.title,
718 "id": related_package.id,
719 "name": related_package.name,
720 }
721 )
722 else:
723 # Non-unified package need to get the unified package and then get the related packages
724 rel = (
725 model.meta.Session.query(model.PackageRelationship)
726 .filter(model.PackageRelationship.object_package_id == pkg_dict["id"])
727 .all()
728 )
730 for r in rel:
731 unified_package = model.Package.get(r.subject_package_id)
732 related_packages.append(
733 {
734 "title": unified_package.title,
735 "id": unified_package.id,
736 "name": unified_package.name,
737 }
738 )
740 rel2 = (
741 model.meta.Session.query(model.PackageRelationship)
742 .filter(
743 model.PackageRelationship.subject_package_id
744 == unified_package.id
745 )
746 .filter(
747 model.PackageRelationship.object_package_id != pkg_dict["id"]
748 )
749 .all()
750 )
752 for r in rel2:
753 related_package = model.Package.get(r.object_package_id)
754 # Check if exists
755 if related_package.id not in set(
756 [p["id"] for p in related_packages]
757 ):
758 related_packages.append(
759 {
760 "title": related_package.title,
761 "id": related_package.id,
762 "name": related_package.name,
763 }
764 )
766 pkg_dict["related_packages"] = related_packages
768 def before_dataset_search(self, params: dict[str, Any]) -> dict[str, Any]:
769 # print(chalk.red("before_dataset_search"))
770 # print(search_params)
771 # L = get_current_lang()
772 # default = get_udc_langs()[0]
774 # qf = [
775 # f"title_{L}_txt^6",
776 # f"notes_{L}_txt^3",
777 # # include if you indexed tags_{L}_txt in index.py:
778 # f"tags_{L}_txt^1",
779 # ]
780 # for name in self.text_fields:
781 # qf.append(f"{name}_{L}_txt^2")
783 # if L != default:
784 # qf += [
785 # f"title_{default}_txt^2",
786 # f"notes_{default}_txt^1",
787 # # f"tags_{default}_txt^0.5",
788 # ]
789 # for name in self.text_fields:
790 # qf.append(f"{name}_{default}_txt^1")
792 # params['defType'] = 'edismax'
793 # params['qf'] = ' '.join(qf)
794 # params.setdefault('pf', f"title_{L}_txt^8")
795 # params.setdefault('qs', '2')
796 return params
798 def after_dataset_search(
799 self, search_results: dict[str, Any], search_params: dict[str, Any]
800 ) -> dict[str, Any]:
801 return search_results
803 def before_dataset_index(self, pkg_dict: dict[str, Any]) -> dict[str, Any]:
804 return _before_dataset_index(pkg_dict)
806 def before_dataset_view(self, pkg_dict: dict[str, Any]) -> dict[str, Any]:
807 return pkg_dict
809 # IMiddleware
810 def make_middleware(self, app: CKANApp, config: CKANConfig) -> CKANApp:
811 override_error_handler(app, config)
812 return app
814 def make_error_log_middleware(self, app, config: CKANConfig) -> CKANApp:
815 return app
817 def get_validators(self):
818 return {
819 "udc_lang_object": udc_lang_object,
820 "udc_json_dump": udc_json_dump,
821 "udc_json_load": udc_json_load,
822 }