oarepo-runtime 1.10.3__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.
- oarepo_runtime/__init__.py +24 -0
- oarepo_runtime/api.py +111 -0
- oarepo_runtime/cli/__init__.py +10 -21
- oarepo_runtime/cli/search.py +34 -0
- oarepo_runtime/config.py +86 -13
- oarepo_runtime/ext.py +64 -82
- oarepo_runtime/proxies.py +21 -5
- oarepo_runtime/records/__init__.py +11 -50
- oarepo_runtime/records/drafts.py +24 -18
- oarepo_runtime/records/mapping.py +84 -0
- oarepo_runtime/records/pid_providers.py +43 -7
- oarepo_runtime/records/systemfields/__init__.py +15 -33
- oarepo_runtime/records/systemfields/mapping.py +41 -24
- oarepo_runtime/records/systemfields/publication_status.py +59 -0
- oarepo_runtime/services/__init__.py +12 -0
- oarepo_runtime/services/config/__init__.py +15 -21
- oarepo_runtime/services/config/link_conditions.py +69 -75
- oarepo_runtime/services/config/permissions.py +62 -0
- oarepo_runtime/services/records/__init__.py +14 -1
- oarepo_runtime/services/records/links.py +21 -11
- oarepo_runtime/services/records/mapping.py +42 -0
- oarepo_runtime/services/results.py +98 -109
- oarepo_runtime/services/schema/__init__.py +12 -44
- oarepo_runtime/services/schema/i18n.py +47 -22
- oarepo_runtime/services/schema/i18n_ui.py +61 -24
- {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/METADATA +9 -21
- oarepo_runtime-2.0.0.dev3.dist-info/RECORD +30 -0
- {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/WHEEL +1 -2
- oarepo_runtime-2.0.0.dev3.dist-info/entry_points.txt +5 -0
- oarepo_runtime/cli/assets.py +0 -145
- oarepo_runtime/cli/base.py +0 -25
- oarepo_runtime/cli/cf.py +0 -15
- oarepo_runtime/cli/check.py +0 -167
- oarepo_runtime/cli/configuration.py +0 -51
- oarepo_runtime/cli/fixtures.py +0 -167
- oarepo_runtime/cli/index.py +0 -272
- oarepo_runtime/cli/permissions/__init__.py +0 -6
- oarepo_runtime/cli/permissions/base.py +0 -26
- oarepo_runtime/cli/permissions/evaluate.py +0 -63
- oarepo_runtime/cli/permissions/list.py +0 -239
- oarepo_runtime/cli/permissions/search.py +0 -121
- oarepo_runtime/cli/validate.py +0 -150
- oarepo_runtime/datastreams/__init__.py +0 -38
- oarepo_runtime/datastreams/asynchronous.py +0 -247
- oarepo_runtime/datastreams/catalogue.py +0 -150
- oarepo_runtime/datastreams/datastreams.py +0 -152
- oarepo_runtime/datastreams/errors.py +0 -54
- oarepo_runtime/datastreams/ext.py +0 -41
- oarepo_runtime/datastreams/fixtures.py +0 -265
- oarepo_runtime/datastreams/json.py +0 -4
- oarepo_runtime/datastreams/readers/__init__.py +0 -39
- oarepo_runtime/datastreams/readers/attachments.py +0 -51
- oarepo_runtime/datastreams/readers/excel.py +0 -123
- oarepo_runtime/datastreams/readers/json.py +0 -27
- oarepo_runtime/datastreams/readers/service.py +0 -54
- oarepo_runtime/datastreams/readers/yaml.py +0 -14
- oarepo_runtime/datastreams/semi_asynchronous.py +0 -91
- oarepo_runtime/datastreams/synchronous.py +0 -70
- oarepo_runtime/datastreams/transformers.py +0 -18
- oarepo_runtime/datastreams/types.py +0 -323
- oarepo_runtime/datastreams/utils.py +0 -131
- oarepo_runtime/datastreams/writers/__init__.py +0 -21
- oarepo_runtime/datastreams/writers/attachments_file.py +0 -92
- oarepo_runtime/datastreams/writers/attachments_service.py +0 -118
- oarepo_runtime/datastreams/writers/publish.py +0 -70
- oarepo_runtime/datastreams/writers/service.py +0 -175
- oarepo_runtime/datastreams/writers/utils.py +0 -30
- oarepo_runtime/datastreams/writers/validation_errors.py +0 -20
- oarepo_runtime/datastreams/writers/yaml.py +0 -56
- oarepo_runtime/ext_config.py +0 -67
- oarepo_runtime/i18n/__init__.py +0 -3
- oarepo_runtime/info/__init__.py +0 -0
- oarepo_runtime/info/check.py +0 -95
- oarepo_runtime/info/permissions/__init__.py +0 -0
- oarepo_runtime/info/permissions/debug.py +0 -191
- oarepo_runtime/info/views.py +0 -586
- oarepo_runtime/profile.py +0 -60
- oarepo_runtime/records/dumpers/__init__.py +0 -8
- oarepo_runtime/records/dumpers/edtf_interval.py +0 -38
- oarepo_runtime/records/dumpers/multilingual_dumper.py +0 -34
- oarepo_runtime/records/entity_resolvers/__init__.py +0 -13
- oarepo_runtime/records/entity_resolvers/proxies.py +0 -57
- oarepo_runtime/records/mappings/__init__.py +0 -0
- oarepo_runtime/records/mappings/rdm_parent_mapping.json +0 -483
- oarepo_runtime/records/owners/__init__.py +0 -3
- oarepo_runtime/records/owners/registry.py +0 -22
- oarepo_runtime/records/relations/__init__.py +0 -22
- oarepo_runtime/records/relations/base.py +0 -296
- oarepo_runtime/records/relations/internal.py +0 -46
- oarepo_runtime/records/relations/lookup.py +0 -28
- oarepo_runtime/records/relations/pid_relation.py +0 -102
- oarepo_runtime/records/systemfields/featured_file.py +0 -45
- oarepo_runtime/records/systemfields/has_draftcheck.py +0 -47
- oarepo_runtime/records/systemfields/icu.py +0 -371
- oarepo_runtime/records/systemfields/owner.py +0 -115
- oarepo_runtime/records/systemfields/record_status.py +0 -35
- oarepo_runtime/records/systemfields/selectors.py +0 -98
- oarepo_runtime/records/systemfields/synthetic.py +0 -130
- oarepo_runtime/resources/__init__.py +0 -4
- oarepo_runtime/resources/config.py +0 -12
- oarepo_runtime/resources/file_resource.py +0 -15
- oarepo_runtime/resources/json_serializer.py +0 -27
- oarepo_runtime/resources/localized_ui_json_serializer.py +0 -54
- oarepo_runtime/resources/resource.py +0 -53
- oarepo_runtime/resources/responses.py +0 -20
- oarepo_runtime/services/components.py +0 -429
- oarepo_runtime/services/config/draft_link.py +0 -23
- oarepo_runtime/services/config/permissions_presets.py +0 -174
- oarepo_runtime/services/config/service.py +0 -117
- oarepo_runtime/services/custom_fields/__init__.py +0 -80
- oarepo_runtime/services/custom_fields/mappings.py +0 -188
- oarepo_runtime/services/entity/__init__.py +0 -0
- oarepo_runtime/services/entity/config.py +0 -14
- oarepo_runtime/services/entity/schema.py +0 -9
- oarepo_runtime/services/entity/service.py +0 -48
- oarepo_runtime/services/expansions/__init__.py +0 -0
- oarepo_runtime/services/expansions/expandable_fields.py +0 -21
- oarepo_runtime/services/expansions/service.py +0 -4
- oarepo_runtime/services/facets/__init__.py +0 -33
- oarepo_runtime/services/facets/base.py +0 -12
- oarepo_runtime/services/facets/date.py +0 -72
- oarepo_runtime/services/facets/enum.py +0 -11
- oarepo_runtime/services/facets/facet_groups_names.py +0 -17
- oarepo_runtime/services/facets/max_facet.py +0 -13
- oarepo_runtime/services/facets/multilingual_facet.py +0 -33
- oarepo_runtime/services/facets/nested_facet.py +0 -32
- oarepo_runtime/services/facets/params.py +0 -192
- oarepo_runtime/services/facets/year_histogram.py +0 -200
- oarepo_runtime/services/files/__init__.py +0 -8
- oarepo_runtime/services/files/components.py +0 -62
- oarepo_runtime/services/files/service.py +0 -16
- oarepo_runtime/services/generators.py +0 -10
- oarepo_runtime/services/permissions/__init__.py +0 -3
- oarepo_runtime/services/permissions/generators.py +0 -103
- oarepo_runtime/services/relations/__init__.py +0 -0
- oarepo_runtime/services/relations/components.py +0 -15
- oarepo_runtime/services/relations/errors.py +0 -18
- oarepo_runtime/services/relations/mapping.py +0 -38
- oarepo_runtime/services/schema/cf.py +0 -13
- oarepo_runtime/services/schema/i18n_validation.py +0 -7
- oarepo_runtime/services/schema/marshmallow.py +0 -44
- oarepo_runtime/services/schema/marshmallow_to_json_schema.py +0 -72
- oarepo_runtime/services/schema/oneofschema.py +0 -192
- oarepo_runtime/services/schema/polymorphic.py +0 -21
- oarepo_runtime/services/schema/rdm.py +0 -146
- oarepo_runtime/services/schema/rdm_ui.py +0 -156
- oarepo_runtime/services/schema/ui.py +0 -251
- oarepo_runtime/services/schema/validation.py +0 -70
- oarepo_runtime/services/search.py +0 -282
- oarepo_runtime/services/service.py +0 -61
- oarepo_runtime/tasks.py +0 -6
- oarepo_runtime/translations/cs/LC_MESSAGES/messages.mo +0 -0
- oarepo_runtime/translations/cs/LC_MESSAGES/messages.po +0 -95
- oarepo_runtime/translations/default_translations.py +0 -6
- oarepo_runtime/translations/en/LC_MESSAGES/messages.mo +0 -0
- oarepo_runtime/translations/en/LC_MESSAGES/messages.po +0 -97
- oarepo_runtime/translations/messages.pot +0 -100
- oarepo_runtime/uow.py +0 -146
- oarepo_runtime/utils/__init__.py +0 -0
- oarepo_runtime/utils/functools.py +0 -37
- oarepo_runtime/utils/identity_utils.py +0 -35
- oarepo_runtime/utils/index.py +0 -11
- oarepo_runtime/utils/path.py +0 -97
- oarepo_runtime-1.10.3.dist-info/RECORD +0 -163
- oarepo_runtime-1.10.3.dist-info/entry_points.txt +0 -16
- oarepo_runtime-1.10.3.dist-info/top_level.txt +0 -2
- tests/marshmallow_to_json/__init__.py +0 -0
- tests/marshmallow_to_json/test_datacite_ui_schema.py +0 -1410
- tests/marshmallow_to_json/test_simple_schema.py +0 -52
- tests/pkg_data/__init__.py +0 -0
- {oarepo_runtime-1.10.3.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,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,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)
|