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.
- oarepo_runtime/__init__.py +24 -0
- oarepo_runtime/api.py +210 -0
- oarepo_runtime/cli/__init__.py +10 -21
- oarepo_runtime/cli/search.py +34 -0
- oarepo_runtime/config.py +98 -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 +61 -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/facets/__init__.py +12 -33
- oarepo_runtime/services/facets/params.py +45 -110
- 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.dev4.dist-info}/METADATA +10 -21
- oarepo_runtime-2.0.0.dev4.dist-info/RECORD +32 -0
- {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev4.dist-info}/WHEEL +1 -2
- oarepo_runtime-2.0.0.dev4.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/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/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.dev4.dist-info}/licenses/LICENSE +0 -0
@@ -1,296 +0,0 @@
|
|
1
|
-
from functools import cached_property
|
2
|
-
from typing import List
|
3
|
-
|
4
|
-
from invenio_records.dictutils import dict_lookup, dict_set
|
5
|
-
from invenio_records.systemfields.base import SystemField
|
6
|
-
from invenio_records.systemfields.relations import (
|
7
|
-
InvalidCheckValue,
|
8
|
-
InvalidRelationValue,
|
9
|
-
)
|
10
|
-
|
11
|
-
from oarepo_runtime.services.relations.errors import MultipleInvalidRelationErrors
|
12
|
-
from oarepo_runtime.services.relations.mapping import RelationsMapping
|
13
|
-
|
14
|
-
from .lookup import LookupResult, lookup_key
|
15
|
-
|
16
|
-
|
17
|
-
class RelationResult:
|
18
|
-
def __init__(self, field, record, cache) -> None:
|
19
|
-
self.field = field
|
20
|
-
self.record = record
|
21
|
-
self.cache = cache
|
22
|
-
|
23
|
-
def validate(self, raise_first_exception=True):
|
24
|
-
found: List[LookupResult] = lookup_key(self.record, self.field.key)
|
25
|
-
exceptions = []
|
26
|
-
for relation in found:
|
27
|
-
try:
|
28
|
-
resolved_object = self._resolve_lookup_result(relation)
|
29
|
-
|
30
|
-
if self.field.value_check:
|
31
|
-
transformed_data = {}
|
32
|
-
self._get_dereferenced_value(
|
33
|
-
transformed_data, resolved_object, relation
|
34
|
-
)
|
35
|
-
self._value_check(relation.value, transformed_data)
|
36
|
-
except Exception as e:
|
37
|
-
if raise_first_exception:
|
38
|
-
raise
|
39
|
-
exceptions.append((relation, e))
|
40
|
-
if exceptions:
|
41
|
-
raise MultipleInvalidRelationErrors(exceptions)
|
42
|
-
|
43
|
-
def _resolve_lookup_result(self, relation):
|
44
|
-
if not isinstance(relation.value, dict):
|
45
|
-
raise InvalidRelationValue(
|
46
|
-
f"Value at path {relation.path} must be dict, found {relation.value}"
|
47
|
-
)
|
48
|
-
relation_id = self._lookup_id(relation)
|
49
|
-
resolved_object = self.resolve(relation_id)
|
50
|
-
if resolved_object is None:
|
51
|
-
raise InvalidRelationValue(f"Could not resolve relation id {relation_id}.")
|
52
|
-
return resolved_object
|
53
|
-
|
54
|
-
def clean(self):
|
55
|
-
"""Clean the dereferenced attributes inside the record."""
|
56
|
-
found: List[LookupResult] = lookup_key(self.record, self.field.key)
|
57
|
-
for relation in found:
|
58
|
-
self._clean_one(relation)
|
59
|
-
|
60
|
-
def dereference(self):
|
61
|
-
"""Dereference the relation field object inside the record."""
|
62
|
-
found: List[LookupResult] = lookup_key(self.record, self.field.key)
|
63
|
-
for relation in found:
|
64
|
-
self._dereference_one(relation)
|
65
|
-
|
66
|
-
def _clean_one(self, relation: LookupResult):
|
67
|
-
"""
|
68
|
-
Cleans the relation. The default implementation does nothing - the relation has been
|
69
|
-
validated,
|
70
|
-
"""
|
71
|
-
|
72
|
-
def _needs_update_relation_value(self, _relation: LookupResult):
|
73
|
-
"""
|
74
|
-
Returns True if the relation needs resolving and updating value
|
75
|
-
|
76
|
-
:param relation: relation being processed
|
77
|
-
"""
|
78
|
-
return True
|
79
|
-
|
80
|
-
def _add_version_info(self, data, relation: LookupResult, resolved_object):
|
81
|
-
"""
|
82
|
-
Adds versioning info on relation data
|
83
|
-
|
84
|
-
:param data: relation data
|
85
|
-
:param relation: relation being processed
|
86
|
-
:param resolved_object: the object to which this relation points to
|
87
|
-
"""
|
88
|
-
|
89
|
-
def _dereference_one(self, relation: LookupResult):
|
90
|
-
"""Dereference a single object into a dict."""
|
91
|
-
if not self._needs_update_relation_value(relation):
|
92
|
-
return
|
93
|
-
|
94
|
-
data = relation.value
|
95
|
-
obj = self._resolve_lookup_result(relation)
|
96
|
-
self._get_dereferenced_value(data, obj, relation)
|
97
|
-
|
98
|
-
return data
|
99
|
-
|
100
|
-
def _get_dereferenced_value(self, data, obj, relation):
|
101
|
-
# Inject selected key/values from related record into
|
102
|
-
# the current record.
|
103
|
-
|
104
|
-
# From record dictionary
|
105
|
-
if self.field.keys is None:
|
106
|
-
data.update({k: v for k, v in obj.items()})
|
107
|
-
else:
|
108
|
-
new_obj = {}
|
109
|
-
for k in self.field.keys:
|
110
|
-
try:
|
111
|
-
key = self._get_dereference_key(k, relation)
|
112
|
-
target = self._get_dereference_target(k, relation)
|
113
|
-
val = dict_lookup(obj, key)
|
114
|
-
if val:
|
115
|
-
dict_set(new_obj, target, val)
|
116
|
-
except KeyError:
|
117
|
-
pass
|
118
|
-
data.update(new_obj)
|
119
|
-
|
120
|
-
# From record attributes (i.e. system fields)
|
121
|
-
for a in self.field.attrs:
|
122
|
-
data[a] = getattr(obj, a)
|
123
|
-
|
124
|
-
self._add_version_info(data, relation, obj)
|
125
|
-
|
126
|
-
def _get_dereference_key(self, key, _relation: LookupResult):
|
127
|
-
if isinstance(key, dict):
|
128
|
-
return key["key"]
|
129
|
-
return key
|
130
|
-
|
131
|
-
def _get_dereference_target(self, key, _relation: LookupResult):
|
132
|
-
if isinstance(key, dict):
|
133
|
-
return key["target"]
|
134
|
-
return key
|
135
|
-
|
136
|
-
def _value_check(self, value_to_check, object):
|
137
|
-
"""Checks if the value is present in the object."""
|
138
|
-
# TODO: this is way too complicated, should refactor it (and look at the code coverage as well)
|
139
|
-
for key, value in value_to_check.items():
|
140
|
-
if key not in object:
|
141
|
-
raise InvalidCheckValue(f"Invalid key {key}.")
|
142
|
-
if isinstance(value, dict):
|
143
|
-
self._value_check(value, object[key])
|
144
|
-
else:
|
145
|
-
if not isinstance(value, list):
|
146
|
-
raise InvalidCheckValue(
|
147
|
-
f"Invalid value_check value: {value}; it must be " "a list"
|
148
|
-
)
|
149
|
-
elif isinstance(object[key], list):
|
150
|
-
value_exist = set(object[key]).intersection(set(value))
|
151
|
-
if not value_exist:
|
152
|
-
raise InvalidCheckValue(
|
153
|
-
f"Failed cross checking value_check value "
|
154
|
-
f"{value} with record value {object[key]}."
|
155
|
-
)
|
156
|
-
else:
|
157
|
-
if object[key] not in value:
|
158
|
-
raise InvalidCheckValue(
|
159
|
-
f"Failed cross checking value_check value "
|
160
|
-
f"{value} with record value {object[key]}."
|
161
|
-
)
|
162
|
-
|
163
|
-
def _lookup_id(self, relation: LookupResult):
|
164
|
-
relation_id = relation.value.get("id", None)
|
165
|
-
if not relation_id:
|
166
|
-
raise InvalidRelationValue(
|
167
|
-
f"Value at path {relation.path} must contain non-empty 'id' field, found {relation.value}"
|
168
|
-
)
|
169
|
-
return relation_id
|
170
|
-
|
171
|
-
def _store_id(self, relation: LookupResult, relation_id):
|
172
|
-
relation.value["id"] = relation_id
|
173
|
-
|
174
|
-
def resolve(self, id_):
|
175
|
-
raise NotImplementedError("Please implement this method in your subclass")
|
176
|
-
|
177
|
-
|
178
|
-
class UnstrictRelationResult(RelationResult):
|
179
|
-
|
180
|
-
def _resolve_lookup_result(self, relation):
|
181
|
-
if not isinstance(relation.value, dict):
|
182
|
-
raise InvalidRelationValue(
|
183
|
-
f"Value at path {relation.path} must be dict, found {relation.value}"
|
184
|
-
)
|
185
|
-
if "id" not in relation.value:
|
186
|
-
return relation.value
|
187
|
-
relation_id = self._lookup_id(relation)
|
188
|
-
resolved_object = self.resolve(relation_id, relation.value)
|
189
|
-
if resolved_object is None:
|
190
|
-
raise InvalidRelationValue(f"Could not resolve relation id {relation_id}.")
|
191
|
-
return resolved_object
|
192
|
-
|
193
|
-
def _dereference_one(self, relation: LookupResult):
|
194
|
-
"""Dereference a single object into a dict."""
|
195
|
-
if not self._needs_update_relation_value(relation):
|
196
|
-
return
|
197
|
-
|
198
|
-
data = relation.value
|
199
|
-
obj = self._resolve_lookup_result(relation)
|
200
|
-
if type(obj) is not dict:
|
201
|
-
self._get_dereferenced_value(data, obj, relation)
|
202
|
-
|
203
|
-
return data
|
204
|
-
|
205
|
-
|
206
|
-
class Relation:
|
207
|
-
result_cls = RelationResult
|
208
|
-
|
209
|
-
def __init__(
|
210
|
-
self,
|
211
|
-
key=None,
|
212
|
-
attrs=None,
|
213
|
-
keys=None,
|
214
|
-
_clear_empty=True,
|
215
|
-
value_check=None,
|
216
|
-
):
|
217
|
-
"""Initialize the relation."""
|
218
|
-
self.key = key
|
219
|
-
self.attrs = attrs or []
|
220
|
-
self.keys = keys or []
|
221
|
-
self._clear_empty = _clear_empty
|
222
|
-
self.value_check = value_check
|
223
|
-
|
224
|
-
def get_value(self, record, cache):
|
225
|
-
"""Return the resolved relation from a record."""
|
226
|
-
return self.result_cls(self, record, cache)
|
227
|
-
|
228
|
-
|
229
|
-
class RelationsField(SystemField):
|
230
|
-
# taken from RelationsField
|
231
|
-
def __init__(self, **fields):
|
232
|
-
"""Initialize the field."""
|
233
|
-
super().__init__()
|
234
|
-
assert all(isinstance(f, Relation) for f in fields.values())
|
235
|
-
self._original_fields = fields
|
236
|
-
|
237
|
-
def __getattr__(self, name):
|
238
|
-
"""Get a field definition."""
|
239
|
-
if name in self._fields:
|
240
|
-
return self._fields[name]
|
241
|
-
raise AttributeError
|
242
|
-
|
243
|
-
def __iter__(self):
|
244
|
-
"""Iterate over the configured fields."""
|
245
|
-
return iter(getattr(self, f) for f in self._fields)
|
246
|
-
|
247
|
-
def __contains__(self, name):
|
248
|
-
"""Return if a field exists in the configured fields."""
|
249
|
-
return name in self._fields
|
250
|
-
|
251
|
-
#
|
252
|
-
# Properties
|
253
|
-
#
|
254
|
-
@cached_property
|
255
|
-
def _fields(self):
|
256
|
-
"""Get the fields."""
|
257
|
-
return self._original_fields
|
258
|
-
|
259
|
-
#
|
260
|
-
# Helpers
|
261
|
-
#
|
262
|
-
def obj(self, instance):
|
263
|
-
"""Get the relations object."""
|
264
|
-
# Check cache
|
265
|
-
obj = self._get_cache(instance)
|
266
|
-
if obj:
|
267
|
-
return obj
|
268
|
-
obj = RelationsMapping(record=instance, fields=self._fields)
|
269
|
-
self._set_cache(instance, obj)
|
270
|
-
return obj
|
271
|
-
|
272
|
-
#
|
273
|
-
# Data descriptor
|
274
|
-
#
|
275
|
-
def __get__(self, record, owner=None):
|
276
|
-
"""Accessing the attribute."""
|
277
|
-
# Class access
|
278
|
-
if record is None:
|
279
|
-
return self
|
280
|
-
return self.obj(record)
|
281
|
-
|
282
|
-
def __set__(self, instance, values):
|
283
|
-
"""Setting the attribute."""
|
284
|
-
obj = self.obj(instance)
|
285
|
-
for k, v in values.items():
|
286
|
-
setattr(obj, k, v)
|
287
|
-
|
288
|
-
#
|
289
|
-
# Record extension
|
290
|
-
#
|
291
|
-
def pre_commit(self, record):
|
292
|
-
"""Initialise the model field."""
|
293
|
-
obj = self.obj(record)
|
294
|
-
obj.validate()
|
295
|
-
obj.clean()
|
296
|
-
obj.dereference()
|
@@ -1,46 +0,0 @@
|
|
1
|
-
from invenio_records.systemfields.relations import InvalidRelationValue
|
2
|
-
|
3
|
-
from .base import Relation, RelationResult
|
4
|
-
from .lookup import LookupResult, lookup_key
|
5
|
-
|
6
|
-
|
7
|
-
class InternalResult(RelationResult):
|
8
|
-
def resolve(self, id_):
|
9
|
-
related_part = self.field.related_part
|
10
|
-
|
11
|
-
if not related_part:
|
12
|
-
return self.record
|
13
|
-
|
14
|
-
potential_values = list(lookup_key(self.record, related_part))
|
15
|
-
|
16
|
-
if not id_:
|
17
|
-
if len(potential_values) > 1:
|
18
|
-
raise InvalidRelationValue(
|
19
|
-
"Relation returned more than one part at "
|
20
|
-
f"{related_part} but has no id to check those parts against"
|
21
|
-
)
|
22
|
-
return potential_values[0].value
|
23
|
-
|
24
|
-
for rel in potential_values:
|
25
|
-
if not isinstance(rel.value, dict):
|
26
|
-
raise KeyError(
|
27
|
-
f"Related part {related_part} does not point to an array of objects at path "
|
28
|
-
f"{rel.path}- array member is {type(rel.value)}: {rel.value}"
|
29
|
-
)
|
30
|
-
|
31
|
-
if id_ == rel.value.get("id", None):
|
32
|
-
return rel.value
|
33
|
-
|
34
|
-
raise KeyError(f"No data for relation at path {related_part} with id {id_}")
|
35
|
-
|
36
|
-
def _lookup_id(self, relation: LookupResult):
|
37
|
-
relation_id = relation.value.get("id", None)
|
38
|
-
return relation_id
|
39
|
-
|
40
|
-
|
41
|
-
class InternalRelation(Relation):
|
42
|
-
result_cls = InternalResult
|
43
|
-
|
44
|
-
def __init__(self, key=None, related_part=None, **kwargs):
|
45
|
-
super().__init__(key=key, **kwargs)
|
46
|
-
self.related_part = related_part
|
@@ -1,28 +0,0 @@
|
|
1
|
-
import dataclasses
|
2
|
-
from typing import Any, Tuple
|
3
|
-
|
4
|
-
|
5
|
-
@dataclasses.dataclass
|
6
|
-
class LookupResult:
|
7
|
-
path: Tuple[str]
|
8
|
-
value: Any
|
9
|
-
|
10
|
-
|
11
|
-
def lookup_key(record, key):
|
12
|
-
"""more generic version of dict_lookup, for arrays does not look up by index but returns all members of array"""
|
13
|
-
|
14
|
-
def _internal_lookup_key(key, data, path: Tuple):
|
15
|
-
if isinstance(data, (tuple, list)):
|
16
|
-
for idx, d in enumerate(data):
|
17
|
-
yield from _internal_lookup_key(key, d, path + (idx,))
|
18
|
-
return
|
19
|
-
if not key:
|
20
|
-
yield LookupResult(path, data)
|
21
|
-
return
|
22
|
-
if not isinstance(data, dict):
|
23
|
-
return
|
24
|
-
if key[0] in data:
|
25
|
-
yield from _internal_lookup_key(key[1:], data[key[0]], path + (key[0],))
|
26
|
-
|
27
|
-
key = key.split(".")
|
28
|
-
return list(_internal_lookup_key(key, record, tuple()))
|
@@ -1,102 +0,0 @@
|
|
1
|
-
from invenio_db import db
|
2
|
-
|
3
|
-
from oarepo_runtime.services.relations.errors import InvalidRelationError
|
4
|
-
from sqlalchemy.exc import NoResultFound
|
5
|
-
|
6
|
-
from .base import Relation, RelationResult, UnstrictRelationResult
|
7
|
-
from .lookup import LookupResult
|
8
|
-
|
9
|
-
class PIDRelationResult(RelationResult):
|
10
|
-
def resolve(self, id_, data = None):
|
11
|
-
"""Resolve the value using the record class."""
|
12
|
-
# TODO: handle permissions here !!!!!!
|
13
|
-
try:
|
14
|
-
pid_field_context = self.field.pid_field
|
15
|
-
try:
|
16
|
-
pid_type = getattr(pid_field_context, "pid_type", None)
|
17
|
-
except:
|
18
|
-
pass
|
19
|
-
|
20
|
-
if pid_type is None:
|
21
|
-
pid_field = pid_field_context.field
|
22
|
-
if pid_field._provider:
|
23
|
-
if hasattr(pid_field._provider, "pid_type"):
|
24
|
-
pid_type = pid_field._provider.pid_type
|
25
|
-
else:
|
26
|
-
pid_type = self.field.pid_field.record_cls.__name__
|
27
|
-
else:
|
28
|
-
if hasattr(pid_field, "_pid_type"):
|
29
|
-
pid_type = pid_field._pid_type
|
30
|
-
else:
|
31
|
-
pid_type = self.field.pid_field.record_cls.__name__
|
32
|
-
except Exception as e:
|
33
|
-
raise InvalidRelationError(
|
34
|
-
f"PID type for field {self.field.key} has not been found or there was an exception accessing it.",
|
35
|
-
related_id=id_,
|
36
|
-
location=self.field.key,
|
37
|
-
) from e
|
38
|
-
|
39
|
-
cache_key = (pid_type, id_)
|
40
|
-
if cache_key in self.cache:
|
41
|
-
obj = self.cache[cache_key]
|
42
|
-
return obj
|
43
|
-
|
44
|
-
try:
|
45
|
-
obj = pid_field_context.resolve(id_)
|
46
|
-
if hasattr(obj, "relations") and obj.relations and hasattr(obj.relations, "dereference"):
|
47
|
-
obj.relations.dereference()
|
48
|
-
# We detach the related record model from the database session when
|
49
|
-
# we add it in the cache. Otherwise, accessing the cached record
|
50
|
-
# model, will execute a new select query after a db.session.commit.
|
51
|
-
db.session.expunge(obj.model)
|
52
|
-
self.cache[cache_key] = obj
|
53
|
-
return obj
|
54
|
-
except Exception as e:
|
55
|
-
raise InvalidRelationError(
|
56
|
-
f"Repository object {cache_key} has not been found or there was an exception accessing it. "
|
57
|
-
f"Referenced from {self.field.key}.",
|
58
|
-
related_id=id_,
|
59
|
-
location=self.field.key,
|
60
|
-
) from e
|
61
|
-
|
62
|
-
def _needs_update_relation_value(self, relation: LookupResult):
|
63
|
-
# Don't dereference if already referenced.
|
64
|
-
return "@v" not in relation.value
|
65
|
-
|
66
|
-
def _add_version_info(self, data, relation: LookupResult, resolved_object):
|
67
|
-
data["@v"] = f"{resolved_object.id}::{resolved_object.revision_id}"
|
68
|
-
|
69
|
-
class UnstrictPIDRelationResult(PIDRelationResult, UnstrictRelationResult):
|
70
|
-
|
71
|
-
def resolve(self, id_, data):
|
72
|
-
try:
|
73
|
-
return super().resolve(id_, data)
|
74
|
-
except InvalidRelationError as e:
|
75
|
-
if isinstance(e.__cause__, NoResultFound):
|
76
|
-
return data
|
77
|
-
raise
|
78
|
-
|
79
|
-
class PIDRelation(Relation):
|
80
|
-
result_cls = PIDRelationResult
|
81
|
-
|
82
|
-
def __init__(self, key=None, pid_field=None, **kwargs):
|
83
|
-
super().__init__(key=key, **kwargs)
|
84
|
-
self.pid_field = pid_field
|
85
|
-
|
86
|
-
class UnstrictPIDRelation(Relation):
|
87
|
-
result_cls = UnstrictPIDRelationResult
|
88
|
-
|
89
|
-
def __init__(self, key=None, pid_field=None, **kwargs):
|
90
|
-
super().__init__(key=key, **kwargs)
|
91
|
-
self.pid_field = pid_field
|
92
|
-
|
93
|
-
class MetadataRelationResult(PIDRelationResult):
|
94
|
-
def _dereference_one(self, relation: LookupResult):
|
95
|
-
ret = super()._dereference_one(relation)
|
96
|
-
if "metadata" in ret:
|
97
|
-
ret.update(ret.pop("metadata"))
|
98
|
-
return ret
|
99
|
-
|
100
|
-
|
101
|
-
class MetadataPIDRelation(PIDRelation):
|
102
|
-
result_cls = MetadataRelationResult
|
@@ -1,45 +0,0 @@
|
|
1
|
-
from invenio_access.permissions import system_identity
|
2
|
-
from invenio_records.systemfields import SystemField
|
3
|
-
from invenio_records_resources.proxies import current_service_registry
|
4
|
-
|
5
|
-
from oarepo_runtime.datastreams.utils import get_file_service_for_record_service
|
6
|
-
from oarepo_runtime.records.systemfields.mapping import MappingSystemFieldMixin
|
7
|
-
|
8
|
-
|
9
|
-
class FeaturedFileFieldResult:
|
10
|
-
def __init__(self, record=None):
|
11
|
-
super().__init__()
|
12
|
-
self.record = record
|
13
|
-
|
14
|
-
|
15
|
-
class FeaturedFileField(MappingSystemFieldMixin, SystemField):
|
16
|
-
def __init__(self, source_field):
|
17
|
-
super(FeaturedFileField, self).__init__(source_field)
|
18
|
-
|
19
|
-
def __get__(self, instance, owner):
|
20
|
-
if instance is None:
|
21
|
-
return self
|
22
|
-
result = FeaturedFileFieldResult(record=instance)
|
23
|
-
return result
|
24
|
-
|
25
|
-
def search_dump(self, data, record):
|
26
|
-
for service in current_service_registry._services:
|
27
|
-
if getattr(
|
28
|
-
current_service_registry._services[service], "record_cls"
|
29
|
-
) == type(record):
|
30
|
-
file_service = get_file_service_for_record_service(
|
31
|
-
record_service=current_service_registry._services[service],
|
32
|
-
record=record,
|
33
|
-
)
|
34
|
-
|
35
|
-
files = file_service.list_files(system_identity, record["id"])
|
36
|
-
file_list = list(files.entries)
|
37
|
-
|
38
|
-
for file in file_list:
|
39
|
-
if (
|
40
|
-
file["metadata"]
|
41
|
-
and "featured" in file["metadata"]
|
42
|
-
and file["metadata"]["featured"]
|
43
|
-
):
|
44
|
-
record["metadata"].update({"featured": file})
|
45
|
-
record.commit()
|
@@ -1,47 +0,0 @@
|
|
1
|
-
from inspect import isfunction
|
2
|
-
|
3
|
-
from invenio_records.dictutils import dict_set
|
4
|
-
from invenio_records.systemfields import SystemField
|
5
|
-
from sqlalchemy.orm.exc import NoResultFound
|
6
|
-
|
7
|
-
from oarepo_runtime.services.custom_fields import CustomFieldsMixin
|
8
|
-
|
9
|
-
|
10
|
-
# taken from https://github.com/inveniosoftware/invenio-rdm-records/blob/master/invenio_rdm_records/records/systemfields/has_draftcheck.py
|
11
|
-
class HasDraftCheckField(CustomFieldsMixin, SystemField):
|
12
|
-
"""PID status field which checks against an expected status."""
|
13
|
-
|
14
|
-
def __init__(self, draft_cls=None, key=None, **kwargs):
|
15
|
-
"""Initialize the PIDField.
|
16
|
-
|
17
|
-
It stores the `record.has_draft` value in the secondary storage
|
18
|
-
system's record or defaults to `False` if the `draft_cls` is not passed
|
19
|
-
e.g Draft records.
|
20
|
-
|
21
|
-
:param key: Attribute name of the HasDraftCheckField.
|
22
|
-
:param draft_cls: The draft class to use for querying.
|
23
|
-
"""
|
24
|
-
super().__init__(key=key, **kwargs)
|
25
|
-
self.draft_cls = draft_cls
|
26
|
-
|
27
|
-
#
|
28
|
-
# Data descriptor methods (i.e. attribute access)
|
29
|
-
#
|
30
|
-
def __get__(self, record, owner=None):
|
31
|
-
if record is None:
|
32
|
-
return self # returns the field itself.
|
33
|
-
|
34
|
-
# If not draft_cls is passed return False
|
35
|
-
if self.draft_cls is None:
|
36
|
-
return False
|
37
|
-
|
38
|
-
try:
|
39
|
-
if isfunction(self.draft_cls):
|
40
|
-
self.draft_cls = self.draft_cls()
|
41
|
-
self.draft_cls.get_record(record.id)
|
42
|
-
return True
|
43
|
-
except NoResultFound:
|
44
|
-
return False
|
45
|
-
|
46
|
-
def pre_dump(self, record, data, **kwargs):
|
47
|
-
dict_set(data, self.key, record.has_draft)
|