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.
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.3.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.3.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 -146
  146. oarepo_runtime/services/schema/rdm_ui.py +0 -156
  147. oarepo_runtime/services/schema/ui.py +0 -251
  148. oarepo_runtime/services/schema/validation.py +0 -70
  149. oarepo_runtime/services/search.py +0 -282
  150. oarepo_runtime/services/service.py +0 -61
  151. oarepo_runtime/tasks.py +0 -6
  152. oarepo_runtime/translations/cs/LC_MESSAGES/messages.mo +0 -0
  153. oarepo_runtime/translations/cs/LC_MESSAGES/messages.po +0 -95
  154. oarepo_runtime/translations/default_translations.py +0 -6
  155. oarepo_runtime/translations/en/LC_MESSAGES/messages.mo +0 -0
  156. oarepo_runtime/translations/en/LC_MESSAGES/messages.po +0 -97
  157. oarepo_runtime/translations/messages.pot +0 -100
  158. oarepo_runtime/uow.py +0 -146
  159. oarepo_runtime/utils/__init__.py +0 -0
  160. oarepo_runtime/utils/functools.py +0 -37
  161. oarepo_runtime/utils/identity_utils.py +0 -35
  162. oarepo_runtime/utils/index.py +0 -11
  163. oarepo_runtime/utils/path.py +0 -97
  164. oarepo_runtime-1.10.3.dist-info/RECORD +0 -163
  165. oarepo_runtime-1.10.3.dist-info/entry_points.txt +0 -16
  166. oarepo_runtime-1.10.3.dist-info/top_level.txt +0 -2
  167. tests/marshmallow_to_json/__init__.py +0 -0
  168. tests/marshmallow_to_json/test_datacite_ui_schema.py +0 -1410
  169. tests/marshmallow_to_json/test_simple_schema.py +0 -52
  170. tests/pkg_data/__init__.py +0 -0
  171. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
@@ -1,371 +0,0 @@
1
- from abc import abstractmethod, abstractproperty
2
- from functools import cached_property
3
- from typing import Dict
4
-
5
- from flask import current_app
6
- from invenio_records.systemfields import SystemField
7
-
8
- from oarepo_runtime.records.relations.lookup import lookup_key
9
- from oarepo_runtime.records.systemfields.mapping import MappingSystemFieldMixin
10
-
11
-
12
- class ICUBase(MappingSystemFieldMixin, SystemField):
13
- """
14
- Base class for ICU system fields.
15
- It provides the basic functionality for ICU fields, such as
16
- getting the attribute name and handling the key.
17
- """
18
-
19
- def __init__(self, source_field=None, key=None):
20
- super().__init__(key=key)
21
- self._attr_name = key or self.__class__.__name__.lower()
22
- self.source_field = source_field
23
-
24
- @cached_property
25
- def languages(self) -> Dict[str, Dict]:
26
- icu_languages = current_app.config.get("OAREPO_ICU_LANGUAGES", {})
27
- if icu_languages:
28
- return icu_languages
29
-
30
- primary_language = current_app.config.get("BABEL_DEFAULT_LOCALE", "en")
31
- # list of tuples [lang, title], just take lang
32
- babel_languages = [x[0] for x in current_app.config.get("I18N_LANGUAGES", [])]
33
-
34
- return {primary_language: {}, **{k: {} for k in babel_languages}}
35
-
36
- def get_values(self, data, language):
37
- ret = []
38
- for l in lookup_key(data, f"{self.source_field}"):
39
- if isinstance(l.value, str):
40
- # take single value as being always the the language provided
41
- ret.append(l.value)
42
- elif isinstance(l.value, dict):
43
- # expected to be {"cs": "", "en": ""}
44
- val = l.value.get(language)
45
- if val:
46
- ret.append(val)
47
- elif "lang" in l.value:
48
- # for [{"lang": "", "value": ""}, ...] we get each item separately
49
- # that's why we do not iterate over l.value
50
- if l.value["lang"] == language:
51
- ret.append(l.value["value"])
52
- return ret
53
-
54
- @abstractproperty
55
- def mapping(self):
56
- """
57
- The mapping for the field. It should return a dictionary with the
58
- mapping for the field, based on the current configuration of the application.
59
- """
60
- raise NotImplementedError("Subclasses must implement the mapping property.")
61
-
62
- @abstractmethod
63
- def search_dump(self, data, record):
64
- """
65
- Dump custom field. This method should be implemented by subclasses
66
- to provide the functionality for dumping the field data into the
67
- OpenSearch data structure.
68
- """
69
- raise NotImplementedError("Subclasses must implement the search_dump method.")
70
-
71
- def search_load(self, data, record_cls):
72
- """
73
- Just remove the field from the data on load.
74
- """
75
- data.pop(self.attr_name, None)
76
-
77
- def __get__(self, instance, owner):
78
- return self
79
-
80
-
81
- class ICUField(ICUBase):
82
- """
83
- A system field that acts as an opensearch "proxy" to another field.
84
- It creates a top-level mapping field with the same name and copies
85
- content of {another field}.language into {mapping field}.language.
86
-
87
- The language accessor can be modified by overriding get_values method.
88
- """
89
-
90
- def __init__(self, *, source_field, key=None):
91
- super().__init__(source_field=source_field, key=key)
92
-
93
- def search_dump(self, data, record):
94
- ret = {}
95
- for lang in self.languages:
96
- r = self.get_values(data, lang)
97
- if r:
98
- # if the language is not empty, add it to the result
99
- # otherwise do not add it at all to safe transport
100
- ret[lang] = r
101
- if ret:
102
- data[self.attr_name] = ret
103
-
104
-
105
- class ICUSortField(ICUField):
106
- """
107
- A field that adds icu sorting field
108
- """
109
-
110
- def __init__(self, *, source_field, key=None):
111
- super().__init__(source_field=source_field, key=key)
112
-
113
- @property
114
- def mapping(self):
115
- return {
116
- self.attr_name: {
117
- "type": "object",
118
- "properties": {
119
- lang: {
120
- "type": "icu_collation_keyword",
121
- "index": False,
122
- "language": lang,
123
- **setting.get("collation", {}),
124
- }
125
- for lang, setting in self.languages.items()
126
- },
127
- },
128
- }
129
-
130
-
131
- class ICUSuggestField(ICUField):
132
- """
133
- A field that adds icu-aware suggestion field
134
- """
135
-
136
- def __init__(self, source_field, key=None):
137
- super().__init__(source_field=source_field, key=key)
138
-
139
- @property
140
- def mapping(self):
141
- return {
142
- self.attr_name: {
143
- "type": "object",
144
- "properties": {
145
- lang: setting.get(
146
- "suggest",
147
- {
148
- "type": "text",
149
- "fields": {
150
- "original": {
151
- "type": "search_as_you_type",
152
- },
153
- "no_accent": {
154
- "type": "search_as_you_type",
155
- "analyzer": "accent_removal_analyzer",
156
- },
157
- },
158
- },
159
- )
160
- for lang, setting in self.languages.items()
161
- },
162
- },
163
- }
164
-
165
- @property
166
- def mapping_settings(self):
167
- return {
168
- "analysis": {
169
- "analyzer": {
170
- "accent_removal_analyzer": {
171
- "type": "custom",
172
- "tokenizer": "standard",
173
- "filter": ["lowercase", "asciifolding"],
174
- }
175
- }
176
- }
177
- }
178
-
179
-
180
- class ICUSearchAnalyzerMixin:
181
-
182
- default_stemming_analyzers = {
183
- "stemming_analyzer_cs": {
184
- "tokenizer": "standard",
185
- "filter": ["stemming_filter_cs", "lowercase"],
186
- },
187
- "stemming_analyzer_en": {
188
- "tokenizer": "standard",
189
- "filter": ["stemming_filter_en", "lowercase"],
190
- },
191
- "ascii_folding_analyzer": {
192
- "tokenizer": "standard",
193
- "filter": ["ascii_folding_filter", "lowercase"],
194
- },
195
- "lowercase_analyzer": {
196
- "tokenizer": "standard",
197
- "filter": ["lowercase"],
198
- },
199
- }
200
-
201
- default_stemming_filters = {
202
- "stemming_filter_cs": {
203
- "type": "stemmer",
204
- "name": "czech",
205
- "language": "czech",
206
- },
207
- "stemming_filter_en": {
208
- "type": "stemmer",
209
- "name": "english",
210
- "language": "english",
211
- },
212
- "ascii_folding_filter": {"type": "asciifolding", "preserve_original": True},
213
- }
214
-
215
- @property
216
- def mapping_settings(self):
217
- return {
218
- "analysis": {
219
- "analyzer": current_app.config.get(
220
- "OAREPO_ICU_SEARCH_ANALYZERS", self.default_stemming_analyzers
221
- ),
222
- "filter": current_app.config.get(
223
- "OAREPO_ICU_SEARCH_FILTERS", self.default_stemming_filters
224
- ),
225
- }
226
- }
227
-
228
-
229
- class ICUSearchField(ICUSearchAnalyzerMixin, ICUField):
230
- """
231
- A field that adds stemming-aware search field for multilingual data (
232
- e.g. data that contains {"cs": "...", "en": "..."}
233
- or [{"lang": "cs", "value": "..."}, ...]
234
- )
235
- """
236
-
237
- def __init__(self, source_field, key=None, boost=1):
238
- super().__init__(source_field=source_field, key=key)
239
- self.boost = boost
240
-
241
- @property
242
- def mapping(self):
243
- return {
244
- self.attr_name: {
245
- "type": "object",
246
- "properties": {
247
- # normal stemming
248
- lang: setting.get(
249
- "search",
250
- {
251
- "type": "text",
252
- "boost": 1 * self.boost,
253
- "fields": {
254
- "stemmed": {
255
- "type": "text",
256
- "analyzer": f"stemming_analyzer_{lang}",
257
- "boost": 0.5 * self.boost,
258
- },
259
- "lowercase": {
260
- "type": "text",
261
- "boost": 0.8 * self.boost,
262
- "analyzer": "lowercase_analyzer",
263
- },
264
- "ascii_folded": {
265
- "type": "text",
266
- "analyzer": "ascii_folding_analyzer",
267
- "boost": 0.3 * self.boost,
268
- },
269
- },
270
- },
271
- )
272
- for lang, setting in self.languages.items()
273
- },
274
- },
275
- }
276
-
277
- def get_values(self, data, language):
278
- return super().get_values(data, language=language)
279
-
280
-
281
- class SingleLanguageSearchField(ICUSearchAnalyzerMixin, ICUBase):
282
- """
283
- A base class for single-language search fields - that is, data contain a text
284
- value in a pre-defined, single language.
285
- """
286
-
287
- def __init__(self, *, source_field, key=None, language=None, boost=1):
288
- super().__init__(source_field=source_field, key=key)
289
- self.language = language
290
- self.boost = boost
291
-
292
- def search_dump(self, data, record):
293
- """Dump custom field."""
294
- ret = self.get_values(data, language=self.language)
295
- if ret:
296
- data[self.attr_name] = ret
297
-
298
-
299
- class FulltextIndexField(SingleLanguageSearchField):
300
- """
301
- A system field that makes the field searchable in OpenSearch,
302
- regardless if it is indexed/analyzed, embedded in Nested or not.
303
-
304
- It creates a top-level mapping field and copies
305
- content of {source_field} into it. It also provides the correct mapping
306
- for the field based on the current configuration of the application.
307
-
308
- Unlike the ICU, this field is a single-language and the language should
309
- be provided when initializing the field.
310
- It defaults to the BABEL_DEFAULT_LOCALE if not provided.
311
- """
312
-
313
- @property
314
- def mapping(self):
315
- language = self.language or current_app.config.get("BABEL_DEFAULT_LOCALE", "en")
316
- mapping_settings = self.languages.get(language, None)
317
- if mapping_settings:
318
- mapping_settings = mapping_settings.get("search")
319
- if not mapping_settings:
320
- mapping_settings = {
321
- "type": "text",
322
- "boost": 1 * self.boost,
323
- "fields": {
324
- "stemmed": {
325
- "type": "text",
326
- "analyzer": f"stemming_analyzer_{language}",
327
- "boost": 0.5 * self.boost,
328
- },
329
- "lowercase": {
330
- "type": "text",
331
- "boost": 0.8 * self.boost,
332
- "analyzer": "lowercase_analyzer",
333
- },
334
- "ascii_folded": {
335
- "type": "text",
336
- "analyzer": "ascii_folding_analyzer",
337
- "boost": 0.3 * self.boost,
338
- },
339
- },
340
- }
341
-
342
- return {self.attr_name: mapping_settings}
343
-
344
- def search_load(self, data, record_cls):
345
- """Load custom field."""
346
- data.pop(self.attr_name, None)
347
-
348
-
349
- class TermIndexField(SingleLanguageSearchField):
350
- """
351
- A system field that makes the field searchable in OpenSearch,
352
- regardless if it is indexed/analyzed, embedded in Nested or not.
353
-
354
- It creates a top-level mapping field and copies
355
- content of {source_field} into it. It also provides the correct mapping
356
- for the field based on the current configuration of the application.
357
-
358
- Unlike the ICU, this field is a single-language and the language should
359
- be provided when initializing the field.
360
- It defaults to the BABEL_DEFAULT_LOCALE if not provided.
361
- """
362
-
363
- @property
364
- def mapping(self):
365
- mapping_settings = {
366
- "type": "keyword",
367
- "boost": 1 * self.boost,
368
- "ignore_above": 256,
369
- }
370
-
371
- return {self.attr_name: mapping_settings}
@@ -1,115 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Copyright (C) 2021 CERN.
4
- #
5
- # Invenio-Communities is free software; you can redistribute it and/or
6
- # modify it under the terms of the MIT License; see LICENSE file for more
7
- # details.
8
-
9
- """Communities system field."""
10
-
11
- from invenio_records.systemfields import SystemField
12
-
13
- from oarepo_runtime.records.owners import OwnerEntityResolverRegistry
14
- from oarepo_runtime.records.systemfields import MappingSystemFieldMixin
15
-
16
-
17
- class OwnerRelationManager:
18
- def __init__(self, record_id, serialized_owners):
19
- self._serialized_owners = serialized_owners
20
- self._deserialized_owners = None
21
-
22
- # from oarepo_requests utils, dependancy on that would be wrong here, right?
23
- # invenio_requests is ok<
24
-
25
- #
26
- # API
27
- #
28
-
29
- def to_dict(self):
30
- if self._serialized_owners is None:
31
- deserialized_owners = []
32
- for deserialized_owner in self._deserialized_owners or []:
33
- serialized_owner = OwnerEntityResolverRegistry.reference_entity(
34
- deserialized_owner
35
- )
36
- if serialized_owner is None:
37
- raise ValueError(
38
- f"failed serialize owner; owner - {deserialized_owner}"
39
- )
40
- deserialized_owners.append(serialized_owner)
41
- self._serialized_owners = deserialized_owners
42
- return self._serialized_owners
43
-
44
- def _resolve(self):
45
- if self._deserialized_owners is None:
46
- self._deserialized_owners = set()
47
- for ref in self._serialized_owners or []:
48
- self._deserialized_owners.add(
49
- OwnerEntityResolverRegistry.resolve_reference(ref)
50
- )
51
- self._serialized_owners = None
52
-
53
- def add(self, owner):
54
- if owner is None:
55
- return
56
- self._resolve()
57
- self._deserialized_owners.add(owner)
58
-
59
- def remove(self, owner):
60
- if owner is None:
61
- return
62
- self._resolve()
63
- self._deserialized_owners.remove(owner)
64
-
65
- def __iter__(self):
66
- self._resolve()
67
- return iter(self._deserialized_owners)
68
-
69
-
70
- class OwnersField(MappingSystemFieldMixin, SystemField):
71
- """Communites system field for managing relations to communities."""
72
-
73
- def __init__(self, key="owners", manager_cls=None):
74
- """Constructor."""
75
- self._manager_cls = manager_cls or OwnerRelationManager
76
- super().__init__(key=key)
77
-
78
- @property
79
- def mapping(self):
80
- return {
81
- self.attr_name: {
82
- "type": "object",
83
- "properties": {"user": {"type": "keyword", "ignore_above": 256}},
84
- },
85
- }
86
-
87
- def pre_commit(self, record):
88
- """Commit the communities field."""
89
- manager = self.obj(record)
90
- self.set_dictkey(record, manager.to_dict())
91
-
92
- def pre_dump(self, record, data, dumper=None):
93
- """Called before a record is dumped."""
94
- # parent record commit op is not called during update, resulting in the parent not being converted correctly into 'dict', ie. the dict() function in invenio_records.dumpers.base #36 works incorrectly
95
- manager = self.obj(record)
96
- self.set_dictkey(record, manager.to_dict())
97
-
98
- def obj(self, record):
99
- """Get or crate the communities manager."""
100
- # Check cache
101
- obj = self._get_cache(record)
102
- if obj is not None:
103
- return obj
104
-
105
- serialized_owners = self.get_dictkey(record)
106
- # Create manager
107
- obj = self._manager_cls(record.id, serialized_owners)
108
- self._set_cache(record, obj)
109
- return obj
110
-
111
- def __get__(self, record, owner=None):
112
- """Get the persistent identifier."""
113
- if record is None:
114
- return self
115
- return self.obj(record)
@@ -1,35 +0,0 @@
1
- from invenio_records.systemfields import SystemField
2
-
3
- from .mapping import MappingSystemFieldMixin
4
-
5
-
6
- class RecordStatusResult:
7
- def __init__(self, record, attr_name):
8
- self.record = record
9
- self.attr_name = attr_name
10
-
11
-
12
- class RecordStatusSystemField(MappingSystemFieldMixin, SystemField):
13
- @property
14
- def mapping(self):
15
- return {
16
- self.attr_name: {
17
- "type": "keyword",
18
- },
19
- }
20
-
21
- def search_load(self, data, record_cls):
22
- data.pop(self.attr_name, None)
23
-
24
- def search_dump(self, data, record):
25
- if getattr(record, "is_draft"):
26
- data[self.attr_name] = "draft"
27
- else:
28
- data[self.attr_name] = "published"
29
-
30
- def __get__(self, record, owner=None):
31
- """Accessing the attribute."""
32
- # Class access
33
- if record is None:
34
- return self
35
- return RecordStatusResult(record, self.attr_name)
@@ -1,98 +0,0 @@
1
- import dataclasses
2
- from typing import Any, Callable, List, Protocol, Tuple
3
-
4
- from oarepo_runtime.records.relations.lookup import lookup_key
5
-
6
-
7
- class Selector(Protocol):
8
- def select(self, record) -> List[Any]:
9
- return []
10
-
11
-
12
- class PathSelector(Selector):
13
- def __init__(self, *paths):
14
- self.paths = [x.split(".") for x in paths]
15
-
16
- def select(self, record):
17
- ret = []
18
- for path in self.paths:
19
- for rec in getter(record, path):
20
- ret.append(rec)
21
- return ret
22
-
23
-
24
- class FirstItemSelector(PathSelector):
25
- def select(self, record):
26
- for rec in super().select(record):
27
- return [rec]
28
- return []
29
-
30
-
31
- @dataclasses.dataclass
32
- class FilteredSelector(Selector):
33
- """
34
- Selector which filters output of another selector
35
- Example:
36
- FilteredSelector(PathSelector("metadata.creators", "metadata.contributors"),
37
- filter=lambda x: x["nameType"] == "personal", projection="affiliations")
38
-
39
- selects affiliations of creators with nameType personal from following data
40
-
41
- data = {
42
- "metadata": {
43
- "creators": [
44
- {"name": "hugo", "affiliations": ["uni1", "uni2"], "nameType": "personal"},
45
- {"name": "uni3", "nameType": "organizational"},
46
- ]
47
- }
48
- }
49
- """
50
- selector: Selector
51
- filter: Callable[[Any], bool]
52
- projection: Callable[[Any], Any] | str = None
53
-
54
- def select(self, record):
55
- selected = self.selector.select(record)
56
- selected = filter(self.filter, selected)
57
- if self.projection:
58
- ret = []
59
- for select_element in selected:
60
- if isinstance(self.projection, str):
61
- result = [x.value for x in lookup_key(select_element, self.projection)]
62
- else:
63
- result = self.projection(select_element)
64
- if isinstance(result, list):
65
- ret += result
66
- else:
67
- ret.append(result)
68
- else:
69
- ret = list(selected)
70
- return ret
71
-
72
-
73
- @dataclasses.dataclass
74
- class MultiSelector(Selector):
75
- """Selector concatenating outputs of multiple selectors"""
76
-
77
- def __init__(self, *selectors: Selector):
78
- self.selectors = selectors
79
-
80
- def select(self, record):
81
- ret = []
82
- for selector in self.selectors:
83
- ret += selector.select(record)
84
- return ret
85
-
86
-
87
- def getter(data, path: List):
88
- if len(path) == 0:
89
- if isinstance(data, list):
90
- yield from data
91
- else:
92
- yield data
93
- elif isinstance(data, dict):
94
- if path[0] in data:
95
- yield from getter(data[path[0]], path[1:])
96
- elif isinstance(data, list):
97
- for item in data:
98
- yield from getter(item, path)