oarepo-runtime 1.10.2__py3-none-any.whl → 2.0.0.dev3__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 +111 -0
  3. oarepo_runtime/cli/__init__.py +10 -21
  4. oarepo_runtime/cli/search.py +34 -0
  5. oarepo_runtime/config.py +86 -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 +59 -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/records/__init__.py +14 -1
  20. oarepo_runtime/services/records/links.py +21 -11
  21. oarepo_runtime/services/records/mapping.py +42 -0
  22. oarepo_runtime/services/results.py +98 -109
  23. oarepo_runtime/services/schema/__init__.py +12 -44
  24. oarepo_runtime/services/schema/i18n.py +47 -22
  25. oarepo_runtime/services/schema/i18n_ui.py +61 -24
  26. {oarepo_runtime-1.10.2.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/METADATA +9 -21
  27. oarepo_runtime-2.0.0.dev3.dist-info/RECORD +30 -0
  28. {oarepo_runtime-1.10.2.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/WHEEL +1 -2
  29. oarepo_runtime-2.0.0.dev3.dist-info/entry_points.txt +5 -0
  30. oarepo_runtime/cli/assets.py +0 -145
  31. oarepo_runtime/cli/base.py +0 -25
  32. oarepo_runtime/cli/cf.py +0 -15
  33. oarepo_runtime/cli/check.py +0 -167
  34. oarepo_runtime/cli/configuration.py +0 -51
  35. oarepo_runtime/cli/fixtures.py +0 -167
  36. oarepo_runtime/cli/index.py +0 -272
  37. oarepo_runtime/cli/permissions/__init__.py +0 -6
  38. oarepo_runtime/cli/permissions/base.py +0 -26
  39. oarepo_runtime/cli/permissions/evaluate.py +0 -63
  40. oarepo_runtime/cli/permissions/list.py +0 -239
  41. oarepo_runtime/cli/permissions/search.py +0 -121
  42. oarepo_runtime/cli/validate.py +0 -150
  43. oarepo_runtime/datastreams/__init__.py +0 -38
  44. oarepo_runtime/datastreams/asynchronous.py +0 -247
  45. oarepo_runtime/datastreams/catalogue.py +0 -150
  46. oarepo_runtime/datastreams/datastreams.py +0 -152
  47. oarepo_runtime/datastreams/errors.py +0 -54
  48. oarepo_runtime/datastreams/ext.py +0 -41
  49. oarepo_runtime/datastreams/fixtures.py +0 -265
  50. oarepo_runtime/datastreams/json.py +0 -4
  51. oarepo_runtime/datastreams/readers/__init__.py +0 -39
  52. oarepo_runtime/datastreams/readers/attachments.py +0 -51
  53. oarepo_runtime/datastreams/readers/excel.py +0 -123
  54. oarepo_runtime/datastreams/readers/json.py +0 -27
  55. oarepo_runtime/datastreams/readers/service.py +0 -54
  56. oarepo_runtime/datastreams/readers/yaml.py +0 -14
  57. oarepo_runtime/datastreams/semi_asynchronous.py +0 -91
  58. oarepo_runtime/datastreams/synchronous.py +0 -70
  59. oarepo_runtime/datastreams/transformers.py +0 -18
  60. oarepo_runtime/datastreams/types.py +0 -323
  61. oarepo_runtime/datastreams/utils.py +0 -131
  62. oarepo_runtime/datastreams/writers/__init__.py +0 -21
  63. oarepo_runtime/datastreams/writers/attachments_file.py +0 -92
  64. oarepo_runtime/datastreams/writers/attachments_service.py +0 -118
  65. oarepo_runtime/datastreams/writers/publish.py +0 -70
  66. oarepo_runtime/datastreams/writers/service.py +0 -175
  67. oarepo_runtime/datastreams/writers/utils.py +0 -30
  68. oarepo_runtime/datastreams/writers/validation_errors.py +0 -20
  69. oarepo_runtime/datastreams/writers/yaml.py +0 -56
  70. oarepo_runtime/ext_config.py +0 -67
  71. oarepo_runtime/i18n/__init__.py +0 -3
  72. oarepo_runtime/info/__init__.py +0 -0
  73. oarepo_runtime/info/check.py +0 -95
  74. oarepo_runtime/info/permissions/__init__.py +0 -0
  75. oarepo_runtime/info/permissions/debug.py +0 -191
  76. oarepo_runtime/info/views.py +0 -586
  77. oarepo_runtime/profile.py +0 -60
  78. oarepo_runtime/records/dumpers/__init__.py +0 -8
  79. oarepo_runtime/records/dumpers/edtf_interval.py +0 -38
  80. oarepo_runtime/records/dumpers/multilingual_dumper.py +0 -34
  81. oarepo_runtime/records/entity_resolvers/__init__.py +0 -13
  82. oarepo_runtime/records/entity_resolvers/proxies.py +0 -57
  83. oarepo_runtime/records/mappings/__init__.py +0 -0
  84. oarepo_runtime/records/mappings/rdm_parent_mapping.json +0 -483
  85. oarepo_runtime/records/owners/__init__.py +0 -3
  86. oarepo_runtime/records/owners/registry.py +0 -22
  87. oarepo_runtime/records/relations/__init__.py +0 -22
  88. oarepo_runtime/records/relations/base.py +0 -296
  89. oarepo_runtime/records/relations/internal.py +0 -46
  90. oarepo_runtime/records/relations/lookup.py +0 -28
  91. oarepo_runtime/records/relations/pid_relation.py +0 -102
  92. oarepo_runtime/records/systemfields/featured_file.py +0 -45
  93. oarepo_runtime/records/systemfields/has_draftcheck.py +0 -47
  94. oarepo_runtime/records/systemfields/icu.py +0 -371
  95. oarepo_runtime/records/systemfields/owner.py +0 -115
  96. oarepo_runtime/records/systemfields/record_status.py +0 -35
  97. oarepo_runtime/records/systemfields/selectors.py +0 -98
  98. oarepo_runtime/records/systemfields/synthetic.py +0 -130
  99. oarepo_runtime/resources/__init__.py +0 -4
  100. oarepo_runtime/resources/config.py +0 -12
  101. oarepo_runtime/resources/file_resource.py +0 -15
  102. oarepo_runtime/resources/json_serializer.py +0 -27
  103. oarepo_runtime/resources/localized_ui_json_serializer.py +0 -54
  104. oarepo_runtime/resources/resource.py +0 -53
  105. oarepo_runtime/resources/responses.py +0 -20
  106. oarepo_runtime/services/components.py +0 -429
  107. oarepo_runtime/services/config/draft_link.py +0 -23
  108. oarepo_runtime/services/config/permissions_presets.py +0 -174
  109. oarepo_runtime/services/config/service.py +0 -117
  110. oarepo_runtime/services/custom_fields/__init__.py +0 -80
  111. oarepo_runtime/services/custom_fields/mappings.py +0 -188
  112. oarepo_runtime/services/entity/__init__.py +0 -0
  113. oarepo_runtime/services/entity/config.py +0 -14
  114. oarepo_runtime/services/entity/schema.py +0 -9
  115. oarepo_runtime/services/entity/service.py +0 -48
  116. oarepo_runtime/services/expansions/__init__.py +0 -0
  117. oarepo_runtime/services/expansions/expandable_fields.py +0 -21
  118. oarepo_runtime/services/expansions/service.py +0 -4
  119. oarepo_runtime/services/facets/__init__.py +0 -33
  120. oarepo_runtime/services/facets/base.py +0 -12
  121. oarepo_runtime/services/facets/date.py +0 -72
  122. oarepo_runtime/services/facets/enum.py +0 -11
  123. oarepo_runtime/services/facets/facet_groups_names.py +0 -17
  124. oarepo_runtime/services/facets/max_facet.py +0 -13
  125. oarepo_runtime/services/facets/multilingual_facet.py +0 -33
  126. oarepo_runtime/services/facets/nested_facet.py +0 -32
  127. oarepo_runtime/services/facets/params.py +0 -192
  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 -75
  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 -85
  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 -89
  157. oarepo_runtime/translations/messages.pot +0 -91
  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.2.dist-info/RECORD +0 -163
  165. oarepo_runtime-1.10.2.dist-info/entry_points.txt +0 -16
  166. oarepo_runtime-1.10.2.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.2.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
@@ -1,33 +0,0 @@
1
- from flask_babel import get_locale
2
- from invenio_records_resources.services.records.facets import TermsFacet
3
-
4
- class MultilingualFacet(TermsFacet):
5
-
6
- def __init__(self, lang_facets, **kwargs):
7
-
8
- self.lang_facets = lang_facets
9
-
10
- super().__init__(**kwargs)
11
-
12
- def get_aggregation(self):
13
- return self.lang_facets[get_locale().language].get_aggregation()
14
-
15
- def get_value(self, bucket):
16
- """Get key value for a bucket."""
17
- return self.lang_facets[get_locale().language].get_value(bucket)
18
-
19
- def get_label_mapping(self, buckets):
20
- """Overwrite this method to provide custom labelling."""
21
- return self.lang_facets[get_locale().language].get_label_mapping(buckets)
22
-
23
- def get_values(self, data, filter_values):
24
- """Get an unlabelled version of the bucket."""
25
- return self.lang_facets[get_locale().language].get_values(data, filter_values)
26
-
27
- def get_labelled_values(self, data, filter_values):
28
- """Get a labelled version of a bucket."""
29
- return self.lang_facets[get_locale().language].get_labelled_values(data, filter_values)
30
-
31
- def add_filter(self, filter_values):
32
- """Create a terms filter instead of bool containing term filters."""
33
- return self.lang_facets[get_locale().language].add_filter(filter_values)
@@ -1,32 +0,0 @@
1
- from invenio_search.engine import dsl
2
-
3
-
4
- class NestedLabeledFacet(dsl.Facet):
5
- agg_type = "nested"
6
-
7
- def __init__(self, path, nested_facet, label=""):
8
- self._path = path
9
- self._inner = nested_facet
10
- self._label = label
11
- super(NestedLabeledFacet, self).__init__(
12
- path=path,
13
- aggs={
14
- "inner": nested_facet.get_aggregation(),
15
- },
16
- )
17
-
18
- def get_values(self, data, filter_values):
19
- return self._inner.get_values(data.inner, filter_values)
20
-
21
- def add_filter(self, filter_values):
22
- inner_q = self._inner.add_filter(filter_values)
23
- if inner_q:
24
- return dsl.Nested(path=self._path, query=inner_q)
25
-
26
- def get_labelled_values(self, data, filter_values):
27
- """Get a labelled version of a bucket."""
28
- try:
29
- out = data["buckets"]
30
- except KeyError:
31
- out = []
32
- return {"buckets": out, "label": str(self._label)}
@@ -1,192 +0,0 @@
1
- import copy
2
- import logging
3
- import operator
4
- from functools import partial, reduce
5
- from typing import List
6
-
7
- from flask import current_app
8
- from flask_principal import Identity
9
- from invenio_access.permissions import system_user_id
10
- from invenio_app.helpers import obj_or_import_string
11
- from invenio_records_resources.services.records.facets import FacetsResponse
12
- 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
- class FilteredFacetsParam(FacetsParam):
24
- def filter(self, search):
25
- """Apply a post filter on the search."""
26
- if not self._filters:
27
- return search
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)
36
-
37
-
38
- class GroupedFacetsParam(FacetsParam):
39
- def __init__(self, config):
40
- super().__init__(config)
41
- self._facets = {**config.facets}
42
-
43
- @property
44
- def facets(self):
45
- return self._facets
46
-
47
- def identity_facet_groups(self, identity: Identity) -> List[str]:
48
- 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)
53
-
54
- if hasattr(identity, "provides"):
55
- return [need.value for need in identity.provides if need.method == "role"]
56
-
57
- return []
58
-
59
- @property
60
- def facet_groups(self):
61
- if hasattr(self.config, "facet_groups"):
62
- return self.config.facet_groups
63
- return None
64
-
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
-
72
- 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
- return self.facets
78
-
79
- 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
- )
83
- if has_system_user_id or has_system_process_need:
84
- return self.facets
85
-
86
- user_facets = self._filter_user_facets(identity)
87
- return user_facets
88
-
89
- def aggregate(self, search, user_facets):
90
- for name, facet in user_facets.items():
91
- agg = facet.get_aggregation()
92
- search.aggs.bucket(name, agg)
93
-
94
- return search
95
-
96
- def filter(self, search):
97
- """Apply a post filter on the search."""
98
- if not self._filters:
99
- return search
100
-
101
- filters = list(self._filters.values())
102
-
103
- _filter = filters[0]
104
- for f in filters[1:]:
105
- _filter &= f
106
-
107
- return search.filter(_filter).post_filter(_filter)
108
-
109
- def apply(self, identity, search, params):
110
- """Evaluate the facets on the search."""
111
- facets_values = params.pop("facets", {})
112
- for name, values in facets_values.items():
113
- if name in self.facets:
114
- self.add_filter(name, values)
115
-
116
- user_facets = self.identity_facets(identity)
117
- self_copy = copy.copy(self)
118
- self_copy._facets = user_facets
119
- search = search.response_class(FacetsResponse.create_response_cls(self_copy))
120
-
121
- search = self.aggregate(search, user_facets)
122
- search = self.filter(search)
123
-
124
- params.update(self.selected_values)
125
-
126
- return search
127
-
128
- def _filter_user_facets(self, identity: Identity):
129
- user_facets = {}
130
- if not self.facet_groups:
131
- user_facets.update(self.facets)
132
- else:
133
- self.facets.clear() # TODO: why is this needed?
134
- user_facets.update(self.facet_groups.get("default", {}))
135
-
136
- groups = self.identity_facet_groups(identity)
137
- for group in groups:
138
- user_facets.update(self.facet_groups.get(group, {}))
139
- 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,200 +0,0 @@
1
- import re
2
- from typing import Dict, List
3
-
4
- from invenio_records_resources.services.records.facets.facets import LabelledFacetMixin
5
- from invenio_search.engine import dsl
6
-
7
-
8
- class YearAutoHistogramFacet(LabelledFacetMixin, dsl.Facet):
9
- """Histogram facet.
10
-
11
- .. code-block:: python
12
-
13
- facets = {
14
- 'year': IntegerHistogramFacet(
15
- field='year',
16
- label=_('Year'),
17
- size=1000000
18
- )
19
- }
20
-
21
- Usage in the oarepo model together with SyntheticSystemField::
22
- record:
23
- record:
24
- imports:
25
- - import: oarepo_runtime.records.systemfields.SyntheticSystemField
26
- - import: oarepo_runtime.records.systemfields.PathSelector
27
- fields:
28
- year: |
29
- SyntheticSystemField(
30
- selector=PathSelector("metadata.date"),
31
- key="year",
32
- filter=lambda x: len(x) >= 4,
33
- map=lambda x: x[:4]
34
- )
35
- properties:
36
- year:
37
- facets:
38
- facet-class: oarepo_runtime.services.facets.year_histogram.YearAutoHistogramFacet
39
- type: edtf
40
- """
41
-
42
- agg_type = "auto_date_histogram"
43
-
44
- def __init__(self, **kwargs):
45
- self._min_doc_count = kwargs.pop("min_doc_count", 0)
46
- buckets = kwargs.pop("buckets", 20)
47
- # TODO: the minimum interval should be year, but opensearch does not support it yet
48
- super().__init__(
49
- **kwargs, buckets=buckets, format="yyyy", minimum_interval="month"
50
- )
51
-
52
- def get_value_filter(self, filter_value):
53
- if "/" in filter_value:
54
- start, end = filter_value.split("/")
55
- return dsl.query.Range(
56
- _expand__to_dot=False,
57
- **{
58
- self._params["field"]: {
59
- "gte": f"{start}-01-01",
60
- "lte": f"{end}-12-31",
61
- }
62
- },
63
- )
64
- return dsl.query.Term(
65
- _expand__to_dot=False,
66
- **{
67
- self._params["field"]: {
68
- "gte": f"{filter_value}-01-01",
69
- "lte": f"{filter_value}-12-31",
70
- }
71
- },
72
- )
73
-
74
- def add_filter(self, filter_values):
75
- ret = super().add_filter(filter_values)
76
- return ret
77
-
78
- def get_labelled_values(self, data, filter_values):
79
- """Get a labelled version of a bucket."""
80
-
81
- # fix for opensearch bug
82
- data = self.fix_yearly_interval(data)
83
-
84
- interval = data["interval"]
85
-
86
- interval_in_years = int(re.sub(r"\D", "", interval))
87
-
88
- buckets = data["buckets"]
89
-
90
- for bucket in buckets:
91
- bucket["interval"] = interval_in_years
92
-
93
- if self._min_doc_count > 0:
94
- buckets = self._merge_small_buckets(buckets)
95
-
96
- out_buckets = []
97
- for i, bucket in enumerate(buckets):
98
- value = int(bucket["key_as_string"].split("-")[0])
99
-
100
- out_buckets.append(
101
- {
102
- **bucket,
103
- "interval": f"{bucket['interval']}y",
104
- "start": str(value),
105
- }
106
- )
107
- if i > 0:
108
- out_buckets[i - 1]["end"] = str(value - 1)
109
-
110
- if out_buckets:
111
- out_buckets[-1]["end"] = str(
112
- int(out_buckets[-1]["start"]) + interval_in_years - 1
113
- )
114
-
115
- return {
116
- "buckets": out_buckets,
117
- "label": str(self._label),
118
- "interval": interval,
119
- }
120
-
121
- def merge_buckets(self, buckets):
122
- merged = {}
123
-
124
- for bucket in buckets:
125
- key = bucket["key_as_string"]
126
- if key not in merged:
127
- merged[key] = {
128
- "key_as_string": key,
129
- "key": bucket["key"],
130
- "doc_count": 0,
131
- }
132
-
133
- merged[key]["doc_count"] += bucket["doc_count"]
134
-
135
- result = list(merged.values())
136
- return result
137
-
138
- def fix_yearly_interval(self, data) -> Dict:
139
- """
140
- Currently opensearch has a bug that does not allow to set minimum_interval to year.
141
- This function will fix the interval to be yearly if the minimum_interval is has lower value.
142
- """
143
- data = data.to_dict()
144
-
145
- interval = data["interval"]
146
-
147
- if interval.endswith("y"):
148
- # no need to fix the interval, as it is in years
149
- return data
150
-
151
- # make sure it is in years
152
- data["interval"] = "1y"
153
-
154
- buckets = data["buckets"]
155
- data["buckets"] = out_buckets = []
156
-
157
- by_year = {}
158
-
159
- # there might be several buckets returned with the same year - merge them
160
- for bucket in buckets:
161
- key = bucket["key_as_string"]
162
- if key not in by_year:
163
- by_year[key] = {
164
- "key_as_string": key,
165
- "key": bucket["key"],
166
- "doc_count": 0,
167
- }
168
- out_buckets.append(by_year[key])
169
-
170
- by_year[key]["doc_count"] += bucket["doc_count"]
171
-
172
- return data
173
-
174
- def _merge_small_buckets(self, buckets: List[Dict]):
175
- """
176
- Merges small buckets into the previous bucket. If the small bucket is the first one,
177
- merge it with subsequent buckets until the first non-small bucket is found.
178
- """
179
- ret = []
180
- initial_small_buckets = 0
181
- initial_small_interval = 0
182
- for bucket in buckets:
183
- if bucket["doc_count"] < self._min_doc_count:
184
- if ret:
185
- ret[-1]["doc_count"] += bucket["doc_count"]
186
- ret[-1]["interval"] += bucket["interval"]
187
- else:
188
- initial_small_buckets += bucket["doc_count"]
189
- initial_small_interval += bucket["interval"]
190
- else:
191
- ret.append(bucket)
192
-
193
- if ret and initial_small_buckets:
194
- doc_count = ret[0]["doc_count"] + initial_small_buckets
195
- interval = ret[0]["interval"] + initial_small_interval
196
- ret[0] = buckets[0]
197
- ret[0]["doc_count"] = doc_count
198
- ret[0]["interval"] = interval
199
-
200
- return ret
@@ -1,8 +0,0 @@
1
- try:
2
- from invenio_records_resources.services.records.components import (
3
- FilesOptionsComponent as FilesComponent,
4
- )
5
- except ImportError:
6
- from invenio_records_resources.services.records.components import FilesComponent
7
-
8
- __all__ = ("FilesComponent",)
@@ -1,62 +0,0 @@
1
- import mimetypes
2
- import os
3
-
4
- from invenio_records_resources.services.files.components import FileServiceComponent
5
- from marshmallow.exceptions import ValidationError
6
-
7
-
8
- class AllowedFileTypesComponent(FileServiceComponent):
9
- def guess_content_type(self, filename: str | None) -> str | None:
10
- if filename:
11
- return mimetypes.guess_type(filename)[0] or "application/octet-stream"
12
- return None
13
-
14
- @property
15
- def allowed_mimetypes(self):
16
- """Returns files attribute (field) key."""
17
- return getattr(self.service.config, "allowed_mimetypes", [])
18
-
19
- def guess_extension(self, file, mimetype):
20
- """File extension."""
21
- # The ``ext`` property is used to in search to aggregate file types, and we want e.g. both ``.jpeg`` and
22
- # ``.jpg`` to be aggregated under ``.jpg``
23
- ext_guessed = mimetypes.guess_extension(mimetype)
24
-
25
- # Check if a valid extension is guessed and it's not the default mimetype
26
- if ext_guessed is not None and mimetype != "application/octet-stream":
27
- return ext_guessed[1:]
28
-
29
- # Support non-standard file extensions that cannot be guessed
30
- _, ext = os.path.splitext(file)
31
- if ext and len(ext) <= 5:
32
- return ext[1:].lower()
33
-
34
- if ext_guessed:
35
- return ext_guessed[1:]
36
-
37
- @property
38
- def allowed_extensions(self):
39
- """Returns files attribute (field) key."""
40
- return getattr(self.service.config, "allowed_extensions", [])
41
-
42
- def init_files(self, identity, id_, data, uow=None):
43
- """Initialize the file upload for the record."""
44
- list_files = list(data.files.entries)
45
-
46
- for file in list_files:
47
- allowed_type = self.guess_content_type(file)
48
- allowed_ext = self.guess_extension(file, allowed_type)
49
- if (
50
- len(self.allowed_mimetypes) > 0
51
- and allowed_type not in self.allowed_mimetypes
52
- ):
53
- raise ValidationError(
54
- f"Mimetype not supported, supported mimetypes: {self.allowed_mimetypes}"
55
- )
56
- elif (
57
- len(self.allowed_extensions) > 0
58
- and allowed_ext not in self.allowed_extensions
59
- ):
60
- raise ValidationError(
61
- f"Extension not supported, supported extensions: {self.allowed_extensions}"
62
- )
@@ -1,16 +0,0 @@
1
- from invenio_records_resources.services.uow import unit_of_work
2
-
3
- from oarepo_runtime.datastreams.utils import get_record_service_for_file_service
4
-
5
-
6
- class FeaturedFileServiceMixin:
7
- @unit_of_work()
8
- def commit_file(self, identity, id_, file_key, uow=None):
9
- super().commit_file(identity, id_, file_key, uow=uow)
10
-
11
- record = self._get_record(id_, identity, "read", file_key=file_key)
12
- record_service = get_record_service_for_file_service(self, record=record)
13
- indexer = record_service.indexer
14
- if indexer:
15
- indexer.index(record)
16
- indexer.refresh()
@@ -1,10 +0,0 @@
1
- import warnings
2
-
3
- from .permissions import RecordOwners
4
-
5
- warnings.warn(
6
- "oarepo_runtime.services.generators is deprecated, import RecordOwners from "
7
- "oarepo_runtime.services.permissions",
8
- DeprecationWarning,
9
- )
10
- __all__ = ("RecordOwners",)
@@ -1,3 +0,0 @@
1
- from .generators import IfDraftType, RecordOwners, UserWithRole
2
-
3
- __all__ = ("RecordOwners", "UserWithRole", "IfDraftType")
@@ -1,103 +0,0 @@
1
- from typing import Literal
2
-
3
- from flask import current_app
4
- from flask_principal import RoleNeed, UserNeed
5
- from invenio_records_permissions.generators import (
6
- ConditionalGenerator,
7
- Disable,
8
- Generator,
9
- )
10
- from invenio_search.engine import dsl
11
-
12
-
13
- class RecordOwners(Generator):
14
- """Allows record owners."""
15
-
16
- def needs(self, record=None, **kwargs):
17
- """Enabling Needs."""
18
- if record is None:
19
- # 'record' is required, so if not passed we default to empty array,
20
- # i.e. superuser-access.
21
- return []
22
- if current_app.config.get("INVENIO_RDM_ENABLED", False):
23
- owners = getattr(record.parent.access, "owned_by", None)
24
- if owners is not None:
25
- owners_list = owners if isinstance(owners, list) else [owners]
26
- return [UserNeed(owner.owner_id) for owner in owners_list]
27
- else:
28
- owners = getattr(record.parent, "owners", None)
29
- if owners is not None:
30
- return [UserNeed(owner.id) for owner in owners]
31
- return []
32
-
33
- def query_filter(self, identity=None, **kwargs):
34
- """Filters for current identity as owner."""
35
- users = [n.value for n in identity.provides if n.method == "id"]
36
- if users:
37
- if current_app.config.get("INVENIO_RDM_ENABLED", False):
38
- return dsl.Q("terms", **{"parent.access.owned_by.user": users})
39
- else:
40
- return dsl.Q("terms", **{"parent.owners.user": users})
41
- return dsl.Q("match_none")
42
-
43
-
44
- class UserWithRole(Generator):
45
- def __init__(self, *roles):
46
- self.roles = roles
47
-
48
- def needs(self, **kwargs):
49
- return [RoleNeed(role) for role in self.roles]
50
-
51
- def query_filter(self, identity=None, **kwargs):
52
- if not identity:
53
- return dsl.Q("match_none")
54
- for provide in identity.provides:
55
- if provide.method == "role" and provide.value in self.roles:
56
- return dsl.Q("match_all")
57
- return dsl.Q("match_none")
58
-
59
-
60
- class IfDraftType(ConditionalGenerator):
61
- def __init__(
62
- self,
63
- draft_types: (
64
- list[Literal["initial"] | Literal["metadata"] | Literal["new_version"]]
65
- | Literal["initial"]
66
- | Literal["metadata"]
67
- | Literal["new_version"]
68
- ),
69
- then_=None,
70
- else_=None,
71
- ):
72
- if not isinstance(draft_types, (list, tuple)):
73
- draft_types = [draft_types]
74
- self._draft_types = draft_types
75
- if not then_:
76
- then_ = [Disable()]
77
- if not else_:
78
- else_ = [Disable()]
79
- if not isinstance(then_, (list, tuple)):
80
- then_ = [then_]
81
- if not isinstance(else_, (list, tuple)):
82
- else_ = [else_]
83
- super().__init__(then_, else_)
84
-
85
- def _condition(self, record=None, **kwargs):
86
- if not record:
87
- return False
88
-
89
- index = record.versions.index
90
- is_latest = record.versions.is_latest
91
- is_draft = record.is_draft
92
-
93
- if not is_draft:
94
- return False
95
-
96
- if index == 1 and not is_latest:
97
- draft_type = "initial"
98
- elif index > 1 and not is_latest:
99
- draft_type = "new_version"
100
- else:
101
- draft_type = "metadata"
102
-
103
- return draft_type in self._draft_types
File without changes
@@ -1,15 +0,0 @@
1
- from invenio_records_resources.services.records.components import ServiceComponent
2
-
3
- from oarepo_runtime.uow import CachingUnitOfWork
4
-
5
-
6
- class CachingRelationsComponent(ServiceComponent):
7
- def create(self, identity, *, record, **kwargs):
8
- """Create handler."""
9
- # skutecny jmeno relations atributu
10
- if isinstance(self.uow, CachingUnitOfWork) and hasattr(record, "relations"):
11
- record.relations.set_cache(self.uow.cache)
12
-
13
- def update(self, identity, *, record, **kwargs):
14
- """Update handler."""
15
- self.create(identity, record=record, **kwargs)