oarepo-runtime 1.10.3__py3-none-any.whl → 2.0.0.dev4__py3-none-any.whl

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 (171) hide show
  1. oarepo_runtime/__init__.py +24 -0
  2. oarepo_runtime/api.py +210 -0
  3. oarepo_runtime/cli/__init__.py +10 -21
  4. oarepo_runtime/cli/search.py +34 -0
  5. oarepo_runtime/config.py +98 -13
  6. oarepo_runtime/ext.py +64 -82
  7. oarepo_runtime/proxies.py +21 -5
  8. oarepo_runtime/records/__init__.py +11 -50
  9. oarepo_runtime/records/drafts.py +24 -18
  10. oarepo_runtime/records/mapping.py +84 -0
  11. oarepo_runtime/records/pid_providers.py +43 -7
  12. oarepo_runtime/records/systemfields/__init__.py +15 -33
  13. oarepo_runtime/records/systemfields/mapping.py +41 -24
  14. oarepo_runtime/records/systemfields/publication_status.py +61 -0
  15. oarepo_runtime/services/__init__.py +12 -0
  16. oarepo_runtime/services/config/__init__.py +15 -21
  17. oarepo_runtime/services/config/link_conditions.py +69 -75
  18. oarepo_runtime/services/config/permissions.py +62 -0
  19. oarepo_runtime/services/facets/__init__.py +12 -33
  20. oarepo_runtime/services/facets/params.py +45 -110
  21. oarepo_runtime/services/records/__init__.py +14 -1
  22. oarepo_runtime/services/records/links.py +21 -11
  23. oarepo_runtime/services/records/mapping.py +42 -0
  24. oarepo_runtime/services/results.py +98 -109
  25. oarepo_runtime/services/schema/__init__.py +12 -44
  26. oarepo_runtime/services/schema/i18n.py +47 -22
  27. oarepo_runtime/services/schema/i18n_ui.py +61 -24
  28. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev4.dist-info}/METADATA +10 -21
  29. oarepo_runtime-2.0.0.dev4.dist-info/RECORD +32 -0
  30. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev4.dist-info}/WHEEL +1 -2
  31. oarepo_runtime-2.0.0.dev4.dist-info/entry_points.txt +5 -0
  32. oarepo_runtime/cli/assets.py +0 -145
  33. oarepo_runtime/cli/base.py +0 -25
  34. oarepo_runtime/cli/cf.py +0 -15
  35. oarepo_runtime/cli/check.py +0 -167
  36. oarepo_runtime/cli/configuration.py +0 -51
  37. oarepo_runtime/cli/fixtures.py +0 -167
  38. oarepo_runtime/cli/index.py +0 -272
  39. oarepo_runtime/cli/permissions/__init__.py +0 -6
  40. oarepo_runtime/cli/permissions/base.py +0 -26
  41. oarepo_runtime/cli/permissions/evaluate.py +0 -63
  42. oarepo_runtime/cli/permissions/list.py +0 -239
  43. oarepo_runtime/cli/permissions/search.py +0 -121
  44. oarepo_runtime/cli/validate.py +0 -150
  45. oarepo_runtime/datastreams/__init__.py +0 -38
  46. oarepo_runtime/datastreams/asynchronous.py +0 -247
  47. oarepo_runtime/datastreams/catalogue.py +0 -150
  48. oarepo_runtime/datastreams/datastreams.py +0 -152
  49. oarepo_runtime/datastreams/errors.py +0 -54
  50. oarepo_runtime/datastreams/ext.py +0 -41
  51. oarepo_runtime/datastreams/fixtures.py +0 -265
  52. oarepo_runtime/datastreams/json.py +0 -4
  53. oarepo_runtime/datastreams/readers/__init__.py +0 -39
  54. oarepo_runtime/datastreams/readers/attachments.py +0 -51
  55. oarepo_runtime/datastreams/readers/excel.py +0 -123
  56. oarepo_runtime/datastreams/readers/json.py +0 -27
  57. oarepo_runtime/datastreams/readers/service.py +0 -54
  58. oarepo_runtime/datastreams/readers/yaml.py +0 -14
  59. oarepo_runtime/datastreams/semi_asynchronous.py +0 -91
  60. oarepo_runtime/datastreams/synchronous.py +0 -70
  61. oarepo_runtime/datastreams/transformers.py +0 -18
  62. oarepo_runtime/datastreams/types.py +0 -323
  63. oarepo_runtime/datastreams/utils.py +0 -131
  64. oarepo_runtime/datastreams/writers/__init__.py +0 -21
  65. oarepo_runtime/datastreams/writers/attachments_file.py +0 -92
  66. oarepo_runtime/datastreams/writers/attachments_service.py +0 -118
  67. oarepo_runtime/datastreams/writers/publish.py +0 -70
  68. oarepo_runtime/datastreams/writers/service.py +0 -175
  69. oarepo_runtime/datastreams/writers/utils.py +0 -30
  70. oarepo_runtime/datastreams/writers/validation_errors.py +0 -20
  71. oarepo_runtime/datastreams/writers/yaml.py +0 -56
  72. oarepo_runtime/ext_config.py +0 -67
  73. oarepo_runtime/i18n/__init__.py +0 -3
  74. oarepo_runtime/info/__init__.py +0 -0
  75. oarepo_runtime/info/check.py +0 -95
  76. oarepo_runtime/info/permissions/__init__.py +0 -0
  77. oarepo_runtime/info/permissions/debug.py +0 -191
  78. oarepo_runtime/info/views.py +0 -586
  79. oarepo_runtime/profile.py +0 -60
  80. oarepo_runtime/records/dumpers/__init__.py +0 -8
  81. oarepo_runtime/records/dumpers/edtf_interval.py +0 -38
  82. oarepo_runtime/records/dumpers/multilingual_dumper.py +0 -34
  83. oarepo_runtime/records/entity_resolvers/__init__.py +0 -13
  84. oarepo_runtime/records/entity_resolvers/proxies.py +0 -57
  85. oarepo_runtime/records/mappings/__init__.py +0 -0
  86. oarepo_runtime/records/mappings/rdm_parent_mapping.json +0 -483
  87. oarepo_runtime/records/owners/__init__.py +0 -3
  88. oarepo_runtime/records/owners/registry.py +0 -22
  89. oarepo_runtime/records/relations/__init__.py +0 -22
  90. oarepo_runtime/records/relations/base.py +0 -296
  91. oarepo_runtime/records/relations/internal.py +0 -46
  92. oarepo_runtime/records/relations/lookup.py +0 -28
  93. oarepo_runtime/records/relations/pid_relation.py +0 -102
  94. oarepo_runtime/records/systemfields/featured_file.py +0 -45
  95. oarepo_runtime/records/systemfields/has_draftcheck.py +0 -47
  96. oarepo_runtime/records/systemfields/icu.py +0 -371
  97. oarepo_runtime/records/systemfields/owner.py +0 -115
  98. oarepo_runtime/records/systemfields/record_status.py +0 -35
  99. oarepo_runtime/records/systemfields/selectors.py +0 -98
  100. oarepo_runtime/records/systemfields/synthetic.py +0 -130
  101. oarepo_runtime/resources/__init__.py +0 -4
  102. oarepo_runtime/resources/config.py +0 -12
  103. oarepo_runtime/resources/file_resource.py +0 -15
  104. oarepo_runtime/resources/json_serializer.py +0 -27
  105. oarepo_runtime/resources/localized_ui_json_serializer.py +0 -54
  106. oarepo_runtime/resources/resource.py +0 -53
  107. oarepo_runtime/resources/responses.py +0 -20
  108. oarepo_runtime/services/components.py +0 -429
  109. oarepo_runtime/services/config/draft_link.py +0 -23
  110. oarepo_runtime/services/config/permissions_presets.py +0 -174
  111. oarepo_runtime/services/config/service.py +0 -117
  112. oarepo_runtime/services/custom_fields/__init__.py +0 -80
  113. oarepo_runtime/services/custom_fields/mappings.py +0 -188
  114. oarepo_runtime/services/entity/__init__.py +0 -0
  115. oarepo_runtime/services/entity/config.py +0 -14
  116. oarepo_runtime/services/entity/schema.py +0 -9
  117. oarepo_runtime/services/entity/service.py +0 -48
  118. oarepo_runtime/services/expansions/__init__.py +0 -0
  119. oarepo_runtime/services/expansions/expandable_fields.py +0 -21
  120. oarepo_runtime/services/expansions/service.py +0 -4
  121. oarepo_runtime/services/facets/base.py +0 -12
  122. oarepo_runtime/services/facets/date.py +0 -72
  123. oarepo_runtime/services/facets/enum.py +0 -11
  124. oarepo_runtime/services/facets/facet_groups_names.py +0 -17
  125. oarepo_runtime/services/facets/max_facet.py +0 -13
  126. oarepo_runtime/services/facets/multilingual_facet.py +0 -33
  127. oarepo_runtime/services/facets/nested_facet.py +0 -32
  128. oarepo_runtime/services/facets/year_histogram.py +0 -200
  129. oarepo_runtime/services/files/__init__.py +0 -8
  130. oarepo_runtime/services/files/components.py +0 -62
  131. oarepo_runtime/services/files/service.py +0 -16
  132. oarepo_runtime/services/generators.py +0 -10
  133. oarepo_runtime/services/permissions/__init__.py +0 -3
  134. oarepo_runtime/services/permissions/generators.py +0 -103
  135. oarepo_runtime/services/relations/__init__.py +0 -0
  136. oarepo_runtime/services/relations/components.py +0 -15
  137. oarepo_runtime/services/relations/errors.py +0 -18
  138. oarepo_runtime/services/relations/mapping.py +0 -38
  139. oarepo_runtime/services/schema/cf.py +0 -13
  140. oarepo_runtime/services/schema/i18n_validation.py +0 -7
  141. oarepo_runtime/services/schema/marshmallow.py +0 -44
  142. oarepo_runtime/services/schema/marshmallow_to_json_schema.py +0 -72
  143. oarepo_runtime/services/schema/oneofschema.py +0 -192
  144. oarepo_runtime/services/schema/polymorphic.py +0 -21
  145. oarepo_runtime/services/schema/rdm.py +0 -146
  146. oarepo_runtime/services/schema/rdm_ui.py +0 -156
  147. oarepo_runtime/services/schema/ui.py +0 -251
  148. oarepo_runtime/services/schema/validation.py +0 -70
  149. oarepo_runtime/services/search.py +0 -282
  150. oarepo_runtime/services/service.py +0 -61
  151. oarepo_runtime/tasks.py +0 -6
  152. oarepo_runtime/translations/cs/LC_MESSAGES/messages.mo +0 -0
  153. oarepo_runtime/translations/cs/LC_MESSAGES/messages.po +0 -95
  154. oarepo_runtime/translations/default_translations.py +0 -6
  155. oarepo_runtime/translations/en/LC_MESSAGES/messages.mo +0 -0
  156. oarepo_runtime/translations/en/LC_MESSAGES/messages.po +0 -97
  157. oarepo_runtime/translations/messages.pot +0 -100
  158. oarepo_runtime/uow.py +0 -146
  159. oarepo_runtime/utils/__init__.py +0 -0
  160. oarepo_runtime/utils/functools.py +0 -37
  161. oarepo_runtime/utils/identity_utils.py +0 -35
  162. oarepo_runtime/utils/index.py +0 -11
  163. oarepo_runtime/utils/path.py +0 -97
  164. oarepo_runtime-1.10.3.dist-info/RECORD +0 -163
  165. oarepo_runtime-1.10.3.dist-info/entry_points.txt +0 -16
  166. oarepo_runtime-1.10.3.dist-info/top_level.txt +0 -2
  167. tests/marshmallow_to_json/__init__.py +0 -0
  168. tests/marshmallow_to_json/test_datacite_ui_schema.py +0 -1410
  169. tests/marshmallow_to_json/test_simple_schema.py +0 -52
  170. tests/pkg_data/__init__.py +0 -0
  171. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev4.dist-info}/licenses/LICENSE +0 -0
@@ -1,126 +1,120 @@
1
- import warnings
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Link conditions module."""
11
+
12
+ from __future__ import annotations
13
+
2
14
  from abc import abstractmethod
3
15
  from logging import getLogger
16
+ from typing import Any
4
17
 
5
18
  from invenio_pidstore.errors import PIDDoesNotExistError, PIDUnregistered
6
- from invenio_records.api import RecordBase
7
- from invenio_records_resources.records.api import FileRecord, Record
19
+ from invenio_records_resources.records.api import FileRecord, RecordBase
8
20
 
9
- from ...datastreams.utils import (
10
- get_file_service_for_record_class,
11
- get_record_service_for_record,
12
- )
13
- from ...records.drafts import get_draft
21
+ from oarepo_runtime.proxies import current_runtime
22
+ from oarepo_runtime.records.drafts import get_draft
14
23
 
15
24
  log = getLogger(__name__)
16
25
 
17
26
 
18
27
  class Condition:
28
+ """Base class for defining conditions with callable logic."""
29
+
19
30
  @abstractmethod
20
- def __call__(self, obj, ctx: dict):
21
- raise NotImplementedError
31
+ def __call__(self, obj: RecordBase, ctx: dict):
32
+ """Abstract method to be implemented in subclasses to define a condition."""
33
+ raise NotImplementedError # pragma: no cover
22
34
 
23
- def __and__(self, other):
35
+ def __and__(self, other: Any):
36
+ """Combine two conditions using a logical AND."""
24
37
  return type(
25
- "CompositeCondition",
38
+ "And",
26
39
  (Condition,),
27
40
  {"__call__": lambda _, obj, ctx: self(obj, ctx) and other(obj, ctx)},
28
41
  )()
29
42
 
30
- def __or__(self, other):
43
+ def __or__(self, other: Any):
44
+ """Combine two conditions using a logical OR."""
31
45
  return type(
32
- "CompositeCondition",
46
+ "Or",
33
47
  (Condition,),
34
48
  {"__call__": lambda _, obj, ctx: self(obj, ctx) or other(obj, ctx)},
35
49
  )()
36
50
 
37
51
 
38
- class is_published_record(Condition):
39
- """Shortcut for links to determine if record is a published record."""
40
-
41
- def __call__(self, obj: Record, ctx: dict):
42
- return not getattr(obj, "is_draft", False)
43
-
44
-
45
- class is_draft_record(Condition):
46
- """Shortcut for links to determine if record is a draft record."""
47
-
48
- def __call__(self, obj: Record, ctx: dict):
49
- return getattr(obj, "is_draft", False)
50
-
51
-
52
- class has_draft(Condition):
53
- """Shortcut for links to determine if record is either a draft or a published one with a draft associated."""
54
-
55
- def __call__(self, obj: Record, ctx: dict):
56
- if getattr(obj, "is_draft", False):
57
- return True
58
- if getattr(obj, "has_draft", False):
59
- return True
60
- return False
61
-
52
+ class has_permission(Condition): # noqa: N801
53
+ """A condition to check if a user has the specified permission for a given record."""
62
54
 
63
- class has_permission(Condition):
64
- def __init__(self, action_name):
55
+ def __init__(self, action_name: str):
56
+ """Initialize the condition with the specified action name."""
65
57
  self.action_name = action_name
66
58
 
67
59
  def __call__(self, obj: RecordBase, ctx: dict):
60
+ """Evaluate the condition by checking the permission for a given record."""
68
61
  if isinstance(obj, FileRecord):
69
62
  obj = obj.record
70
- service = get_record_service_for_record(obj)
63
+ service = current_runtime.get_record_service_for_record(obj)
71
64
  try:
72
- return service.check_permission(
73
- action_name=self.action_name, record=obj, **ctx
74
- )
75
- except Exception as e:
76
- log.exception(f"Unexpected exception {e}.")
65
+ return service.check_permission(action_name=self.action_name, record=obj, **ctx)
66
+ except Exception: # pragma: no cover
67
+ log.exception("Unexpected exception.")
68
+
77
69
 
70
+ class has_draft_permission(Condition): # noqa: N801
71
+ """A condition to check if a user has the specified permission for a draft record."""
78
72
 
79
- class has_draft_permission(Condition):
80
- def __init__(self, action_name):
73
+ def __init__(self, action_name: str):
74
+ """Initialize the condition with the specified action name."""
81
75
  self.action_name = action_name
82
76
 
83
77
  def __call__(self, obj: RecordBase, ctx: dict):
78
+ """Valuates the condition by checking the permission for a draft record."""
79
+ _ = ctx
84
80
  draft_record = get_draft(obj)
85
81
  if not draft_record:
86
82
  return False
87
- service = get_record_service_for_record(obj)
83
+ service = current_runtime.get_record_service_for_record(obj)
88
84
  try:
89
- return service.check_permission(
90
- action_name=self.action_name, record=draft_record, **ctx
91
- )
92
- except Exception as e:
93
- log.exception(f"Unexpected exception {e}.")
85
+ return service.check_permission(action_name=self.action_name, record=draft_record, **ctx)
86
+ except Exception: # pragma: no cover
87
+ log.exception("Unexpected exception.")
94
88
  return False
95
89
 
96
90
 
97
- class has_file_permission(has_permission):
98
- def __call__(self, obj: RecordBase, ctx: dict):
99
- if isinstance(obj, FileRecord):
100
- obj = obj.record
101
- service = get_file_service_for_record_class(type(obj))
102
- try:
103
- return service.check_permission(
104
- action_name=self.action_name, record=obj, **ctx
105
- )
106
- except Exception as e:
107
- log.exception(f"Unexpected exception {e}.")
91
+ class has_draft(Condition): # noqa: N801
92
+ """Shortcut for links to determine if record is either a draft or a published one with a draft associated."""
108
93
 
94
+ def __call__(self, obj: RecordBase, ctx: dict):
95
+ """Check if the given record has draft."""
96
+ _ = ctx
97
+ return bool(getattr(obj, "is_draft", False)) or bool(getattr(obj, "has_draft", False))
109
98
 
110
- class has_permission_file_service(has_file_permission):
111
- def __init__(self, action_name):
112
- warnings.warn(
113
- "has_permission_file_service is deprecated, use has_file_permission instead",
114
- DeprecationWarning,
115
- )
116
- super().__init__(action_name)
117
99
 
100
+ class has_published_record(Condition): # noqa: N801
101
+ """Shortcut for links to determine if the given record has a published PID."""
118
102
 
119
- class has_published_record(Condition):
120
- def __call__(self, obj: Record, ctx: dict):
121
- service = get_record_service_for_record(obj)
103
+ def __call__(self, obj: RecordBase, ctx: dict):
104
+ """Check if the given record has a published PID."""
105
+ _ = ctx
106
+ service = current_runtime.get_record_service_for_record(obj)
122
107
  try:
123
108
  service.record_cls.pid.resolve(obj["id"])
124
109
  except (PIDUnregistered, PIDDoesNotExistError):
125
110
  return False
126
111
  return True
112
+
113
+
114
+ class is_published_record(Condition): # noqa: N801
115
+ """Shortcut for links to determine if record is a published record."""
116
+
117
+ def __call__(self, obj: RecordBase, ctx: dict):
118
+ """Check if the given record is draft."""
119
+ _ = ctx
120
+ return not getattr(obj, "is_draft", False)
@@ -0,0 +1,62 @@
1
+ # mypy: disable-error-code="assignment"
2
+ #
3
+ # Copyright (c) 2025 CESNET z.s.p.o.
4
+ #
5
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
6
+ #
7
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
8
+ # under the terms of the MIT License; see LICENSE file for more details.
9
+ #
10
+
11
+ """Everyone permissions."""
12
+
13
+ from __future__ import annotations
14
+
15
+ from invenio_records_permissions import RecordPermissionPolicy
16
+ from invenio_records_permissions.generators import AnyUser, Generator, SystemProcess
17
+
18
+ type GeneratorList = tuple[Generator, ...]
19
+
20
+
21
+ class EveryonePermissionPolicy(RecordPermissionPolicy):
22
+ """Record policy for read-only repository."""
23
+
24
+ can_search: GeneratorList = (SystemProcess(), AnyUser())
25
+ can_read: GeneratorList = (SystemProcess(), AnyUser())
26
+ can_create: GeneratorList = (SystemProcess(), AnyUser())
27
+ can_update: GeneratorList = (SystemProcess(), AnyUser())
28
+ can_delete: GeneratorList = (SystemProcess(), AnyUser())
29
+ can_manage: GeneratorList = (SystemProcess(), AnyUser())
30
+
31
+ can_create_files: GeneratorList = (SystemProcess(), AnyUser())
32
+ can_set_content_files: GeneratorList = (SystemProcess(), AnyUser())
33
+ can_get_content_files: GeneratorList = (SystemProcess(), AnyUser())
34
+ can_commit_files: GeneratorList = (SystemProcess(), AnyUser())
35
+ can_read_files: GeneratorList = (SystemProcess(), AnyUser())
36
+ can_update_files: GeneratorList = (SystemProcess(), AnyUser())
37
+ can_delete_files: GeneratorList = (SystemProcess(), AnyUser())
38
+ can_list_files: GeneratorList = (SystemProcess(), AnyUser())
39
+ can_manage_files: GeneratorList = (SystemProcess(), AnyUser())
40
+
41
+ can_edit: GeneratorList = (SystemProcess(), AnyUser())
42
+ can_new_version: GeneratorList = (SystemProcess(), AnyUser())
43
+ can_search_drafts: GeneratorList = (SystemProcess(), AnyUser())
44
+ can_read_draft: GeneratorList = (SystemProcess(), AnyUser())
45
+ can_search_versions: GeneratorList = (SystemProcess(), AnyUser())
46
+ can_update_draft: GeneratorList = (SystemProcess(), AnyUser())
47
+ can_delete_draft: GeneratorList = (SystemProcess(), AnyUser())
48
+ can_publish: GeneratorList = (SystemProcess(), AnyUser())
49
+ can_draft_create_files: GeneratorList = (SystemProcess(), AnyUser())
50
+ can_draft_set_content_files: GeneratorList = (SystemProcess(), AnyUser())
51
+ can_draft_get_content_files: GeneratorList = (SystemProcess(), AnyUser())
52
+ can_draft_commit_files: GeneratorList = (SystemProcess(), AnyUser())
53
+ can_draft_read_files: GeneratorList = (SystemProcess(), AnyUser())
54
+ can_draft_update_files: GeneratorList = (SystemProcess(), AnyUser())
55
+ can_draft_delete_files: GeneratorList = (SystemProcess(), AnyUser())
56
+
57
+ can_add_community: GeneratorList = (SystemProcess(), AnyUser())
58
+ can_remove_community: GeneratorList = (SystemProcess(), AnyUser())
59
+
60
+ can_read_deleted: GeneratorList = (SystemProcess(), AnyUser())
61
+ can_manage_record_access: GeneratorList = (SystemProcess(), AnyUser())
62
+ can_lift_embargo: GeneratorList = (SystemProcess(), AnyUser())
@@ -1,33 +1,12 @@
1
- from .base import LabelledValuesTermsFacet
2
- from .date import (
3
- AutoDateHistogramFacet,
4
- DateFacet,
5
- DateIntervalFacet,
6
- DateTimeFacet,
7
- EDTFIntervalFacet,
8
- TimeFacet,
9
- )
10
- from .enum import EnumTermsFacet
11
- from .facet_groups_names import facet_groups_names
12
- from .max_facet import MaxFacet
13
- from .nested_facet import NestedLabeledFacet
14
- from .params import FilteredFacetsParam, GroupedFacetsParam
15
- from .year_histogram import YearAutoHistogramFacet
16
- from .multilingual_facet import MultilingualFacet
17
- __all__ = [
18
- "LabelledValuesTermsFacet",
19
- "DateFacet",
20
- "TimeFacet",
21
- "DateTimeFacet",
22
- "AutoDateHistogramFacet",
23
- "EDTFIntervalFacet",
24
- "DateIntervalFacet",
25
- "EnumTermsFacet",
26
- "facet_groups_names",
27
- "MaxFacet",
28
- "NestedLabeledFacet",
29
- "GroupedFacetsParam",
30
- "FilteredFacetsParam",
31
- "YearAutoHistogramFacet",
32
- "MultilingualFacet"
33
- ]
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Facet parameter classes."""
11
+
12
+ from __future__ import annotations
@@ -1,55 +1,52 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Facet params."""
11
+
12
+ from __future__ import annotations
13
+
1
14
  import copy
2
15
  import logging
3
- import operator
4
- from functools import partial, reduce
5
- from typing import List
16
+ from typing import TYPE_CHECKING
6
17
 
7
18
  from flask import current_app
8
- from flask_principal import Identity
9
19
  from invenio_access.permissions import system_user_id
10
20
  from invenio_app.helpers import obj_or_import_string
11
21
  from invenio_records_resources.services.records.facets import FacetsResponse
12
22
  from invenio_records_resources.services.records.params import FacetsParam
13
- from invenio_access.permissions import authenticated_user
14
- from invenio_records_resources.services.records.params.base import ParamInterpreter
15
- from invenio_search.engine import dsl
16
- from invenio_rdm_records.records.systemfields.deletion_status import (
17
- RecordDeletionStatusEnum,
18
- )
19
-
20
- log = logging.getLogger(__name__)
21
-
22
23
 
23
- class FilteredFacetsParam(FacetsParam):
24
- def filter(self, search):
25
- """Apply a post filter on the search."""
26
- if not self._filters:
27
- return search
24
+ if TYPE_CHECKING:
25
+ from flask_principal import Identity
26
+ from invenio_records_resources.services.records.config import SearchOptions
27
+ from invenio_search.engine import dsl
28
28
 
29
- filters = list(self._filters.values())
30
-
31
- facet_filter = filters[0]
32
- for f in filters[1:]:
33
- facet_filter &= f
34
-
35
- return search.filter(facet_filter)
29
+ log = logging.getLogger(__name__)
36
30
 
37
31
 
38
32
  class GroupedFacetsParam(FacetsParam):
39
- def __init__(self, config):
33
+ """Facet parameter class that supports grouping of facets."""
34
+
35
+ def __init__(self, config: SearchOptions):
36
+ """Initialize the facets parameter with the given config."""
40
37
  super().__init__(config)
41
38
  self._facets = {**config.facets}
42
39
 
43
40
  @property
44
- def facets(self):
41
+ def facets(self) -> dict[str, dsl.Facet]:
42
+ """Return the facets dictionary."""
45
43
  return self._facets
46
44
 
47
- def identity_facet_groups(self, identity: Identity) -> List[str]:
45
+ def identity_facet_groups(self, identity: Identity) -> list[str]:
46
+ """Return the facet groups for the given identity."""
48
47
  if "OAREPO_FACET_GROUP_NAME" in current_app.config:
49
- find_facet_groups_func = obj_or_import_string(
50
- current_app.config["OAREPO_FACET_GROUP_NAME"]
51
- )
52
- return find_facet_groups_func(identity, self.config, None)
48
+ find_facet_groups_func = obj_or_import_string(current_app.config["OAREPO_FACET_GROUP_NAME"])
49
+ return find_facet_groups_func(identity, self.config, None) # type: ignore[no-any-return]
53
50
 
54
51
  if hasattr(identity, "provides"):
55
52
  return [need.value for need in identity.provides if need.method == "role"]
@@ -57,43 +54,33 @@ class GroupedFacetsParam(FacetsParam):
57
54
  return []
58
55
 
59
56
  @property
60
- def facet_groups(self):
57
+ def facet_groups(self) -> dict[str, dict[str, dsl.Facet]] | None:
58
+ """Return the facet groups defined in the service config."""
61
59
  if hasattr(self.config, "facet_groups"):
62
- return self.config.facet_groups
60
+ return self.config.facet_groups # type: ignore[no-any-return]
63
61
  return None
64
62
 
65
- def identity_facets(self, identity: Identity):
66
- global_search_model = False
67
- for model in current_app.config.get("GLOBAL_SEARCH_MODELS", []):
68
- service_config = obj_or_import_string(model["service_config"])
69
- if service_config == self.config:
70
- global_search_model = True
71
-
63
+ def identity_facets(self, identity: Identity) -> dict[str, dsl.Facet]:
64
+ """Return the facets for the given identity."""
72
65
  if not self.facet_groups:
73
- if global_search_model:
74
- log.warning(
75
- "No facet groups defined on the service config %s", type(self.config)
76
- )
77
66
  return self.facets
78
67
 
79
68
  has_system_user_id = identity.id == system_user_id
80
- has_system_process_need = any(
81
- need.method == "system_process" for need in identity.provides
82
- )
69
+ has_system_process_need = any(need.method == "system_process" for need in identity.provides)
83
70
  if has_system_user_id or has_system_process_need:
84
71
  return self.facets
85
72
 
86
- user_facets = self._filter_user_facets(identity)
87
- return user_facets
73
+ return self._filter_user_facets(identity)
88
74
 
89
- def aggregate(self, search, user_facets):
75
+ def aggregate_with_user_facets(self, search: dsl.Search, user_facets: dict[str, dsl.Facet]) -> dsl.Search:
76
+ """Add aggregations representing the user facets."""
90
77
  for name, facet in user_facets.items():
91
78
  agg = facet.get_aggregation()
92
79
  search.aggs.bucket(name, agg)
93
80
 
94
81
  return search
95
82
 
96
- def filter(self, search):
83
+ def filter(self, search: dsl.Search) -> dsl.Search:
97
84
  """Apply a post filter on the search."""
98
85
  if not self._filters:
99
86
  return search
@@ -106,7 +93,7 @@ class GroupedFacetsParam(FacetsParam):
106
93
 
107
94
  return search.filter(_filter).post_filter(_filter)
108
95
 
109
- def apply(self, identity, search, params):
96
+ def apply(self, identity: Identity, search: dsl.Search, params: dict) -> dsl.Search:
110
97
  """Evaluate the facets on the search."""
111
98
  facets_values = params.pop("facets", {})
112
99
  for name, values in facets_values.items():
@@ -115,17 +102,18 @@ class GroupedFacetsParam(FacetsParam):
115
102
 
116
103
  user_facets = self.identity_facets(identity)
117
104
  self_copy = copy.copy(self)
118
- self_copy._facets = user_facets
105
+ self_copy._facets = user_facets # noqa: SLF001 - TODO: this looks like a hack
119
106
  search = search.response_class(FacetsResponse.create_response_cls(self_copy))
120
107
 
121
- search = self.aggregate(search, user_facets)
108
+ search = self.aggregate_with_user_facets(search, user_facets)
122
109
  search = self.filter(search)
123
110
 
124
111
  params.update(self.selected_values)
125
112
 
126
113
  return search
127
114
 
128
- def _filter_user_facets(self, identity: Identity):
115
+ def _filter_user_facets(self, identity: Identity) -> dict[str, dsl.Facet]:
116
+ """Filter user facets based on the identity."""
129
117
  user_facets = {}
130
118
  if not self.facet_groups:
131
119
  user_facets.update(self.facets)
@@ -135,58 +123,5 @@ class GroupedFacetsParam(FacetsParam):
135
123
 
136
124
  groups = self.identity_facet_groups(identity)
137
125
  for group in groups:
138
- user_facets.update(self.facet_groups.get(group, {}))
126
+ user_facets.update((self.facet_groups or {}).get(group, {}))
139
127
  return user_facets
140
-
141
-
142
- class OARepoAllVersionsParam(ParamInterpreter):
143
- """Evaluates the 'allversions' parameter."""
144
- def __init__(self, field_names, config):
145
- """Construct."""
146
- self.field_names = field_names
147
- super().__init__(config)
148
-
149
- @classmethod
150
- def factory(cls, field_names: list[str]):
151
- """Create a new filter parameter."""
152
- return partial(cls, field_names)
153
-
154
- def apply(self, identity, search, params):
155
- """Evaluate the allversions parameter on the search."""
156
- if not params.get("allversions"):
157
- queries = [dsl.query.Q("term", **{field_name: True}) for field_name in self.field_names]
158
- query = reduce(operator.or_, queries)
159
- search = search.filter(query)
160
- return search
161
-
162
- class OARepoPublishedRecordsParam(ParamInterpreter):
163
- """Evaluates the include_deleted parameter."""
164
-
165
- def apply(self, identity, search, params):
166
- """Evaluate the include_deleted parameter on the search."""
167
-
168
- value = params.pop("include_deleted", None)
169
- # Filter prevents from displaying deleted records on mainsite search
170
- # deleted records should appear only in admins panel
171
- if value is None:
172
- query = dsl.query.Q(
173
- "bool",
174
- should=[
175
- dsl.query.Q(
176
- "bool",
177
- must=[
178
- dsl.query.Q(
179
- "term",
180
- deletion_status=RecordDeletionStatusEnum.PUBLISHED.value,
181
- )
182
- ],
183
- ),
184
- # Drafts does not have deletion_status so this clause is needed to
185
- # prevent the above clause from filtering out the drafts
186
- dsl.query.Q(
187
- "bool", must_not=[dsl.query.Q("exists", field="deletion_status")]
188
- ),
189
- ],
190
- )
191
- search = search.filter(query)
192
- return search
@@ -1,3 +1,16 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Service records module."""
11
+
12
+ from __future__ import annotations
13
+
1
14
  from .links import pagination_links_html
2
15
 
3
- __all__ = ("pagination_links_html",)
16
+ __all__ = ("pagination_links_html",)
@@ -1,21 +1,31 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Utility for rendering URI template links."""
11
+
12
+ from __future__ import annotations
13
+
1
14
  from invenio_records_resources.services.base.links import Link
2
15
 
3
- def pagination_links_html(tpl: str)->dict[str, Link]:
4
- """Create pagination links (prev/selv/next) from the same template."""
16
+
17
+ def pagination_links_html(tpl: str) -> dict[str, Link]:
18
+ """Create pagination links (prev/self/next) from the same template."""
5
19
  return {
6
20
  "prev_html": Link(
7
21
  tpl,
8
- when=lambda pagination, ctx: pagination.has_prev,
9
- vars=lambda pagination, vars: vars["args"].update(
10
- {"page": pagination.prev_page.page}
11
- ),
22
+ when=lambda pagination, _context: pagination.has_prev,
23
+ vars=lambda pagination, variables: variables["args"].update({"page": pagination.prev_page.page}),
12
24
  ),
13
25
  "self_html": Link(tpl),
14
26
  "next_html": Link(
15
27
  tpl,
16
- when=lambda pagination, ctx: pagination.has_next,
17
- vars=lambda pagination, vars: vars["args"].update(
18
- {"page": pagination.next_page.page}
19
- ),
28
+ when=lambda pagination, _context: pagination.has_next,
29
+ vars=lambda pagination, variables: variables["args"].update({"page": pagination.next_page.page}),
20
30
  ),
21
- }
31
+ }
@@ -0,0 +1,42 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+ """Update mappings for all record classes in the service registry."""
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import TYPE_CHECKING
14
+
15
+ from invenio_records_resources.services.records import (
16
+ RecordService,
17
+ RecordServiceConfig,
18
+ )
19
+
20
+ from oarepo_runtime import current_runtime
21
+ from oarepo_runtime.records.mapping import update_record_system_fields_mapping
22
+
23
+ if TYPE_CHECKING:
24
+ from invenio_records_resources.services.base import Service
25
+
26
+
27
+ def update_all_records_mappings() -> None:
28
+ """Update all mappings for the registered record classes."""
29
+ service: Service
30
+ for service in current_runtime.services.values():
31
+ if not isinstance(service, RecordService):
32
+ continue
33
+
34
+ config: RecordServiceConfig = service.config
35
+
36
+ record_class = getattr(config, "record_cls", None)
37
+ if record_class:
38
+ update_record_system_fields_mapping(record_class)
39
+
40
+ draft_class = getattr(config, "draft_cls", None)
41
+ if draft_class:
42
+ update_record_system_fields_mapping(draft_class)