oarepo-runtime 1.4.12__tar.gz → 1.4.13__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. {oarepo-runtime-1.4.12/oarepo_runtime.egg-info → oarepo-runtime-1.4.13}/PKG-INFO +16 -50
  2. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/README.md +15 -49
  3. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cf/__init__.py +32 -2
  4. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cf/cli.py +2 -11
  5. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cf/mappings.py +22 -35
  6. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cli/index.py +0 -1
  7. oarepo-runtime-1.4.13/oarepo_runtime/records/__init__.py +29 -0
  8. oarepo-runtime-1.4.13/oarepo_runtime/records/icu.py +132 -0
  9. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/errors.py +3 -1
  10. oarepo-runtime-1.4.13/oarepo_runtime/services/icu.py +160 -0
  11. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13/oarepo_runtime.egg-info}/PKG-INFO +16 -50
  12. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime.egg-info/SOURCES.txt +3 -2
  13. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/setup.cfg +1 -1
  14. oarepo-runtime-1.4.12/oarepo_runtime/cf/icu.py +0 -105
  15. oarepo-runtime-1.4.12/oarepo_runtime/services/search.py +0 -112
  16. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/LICENSE +0 -0
  17. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/MANIFEST.in +0 -0
  18. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/__init__.py +0 -0
  19. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cli/__init__.py +0 -0
  20. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cli/assets.py +0 -0
  21. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cli/base.py +0 -0
  22. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cli/check.py +0 -0
  23. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/cli/validate.py +0 -0
  24. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/config/__init__.py +0 -0
  25. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/config/permissions_presets.py +0 -0
  26. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/config/service.py +0 -0
  27. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/__init__.py +0 -0
  28. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/batch.py +0 -0
  29. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/catalogue.py +0 -0
  30. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/cli.py +0 -0
  31. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/config.py +0 -0
  32. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/datastreams.py +0 -0
  33. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/errors.py +0 -0
  34. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/fixtures.py +0 -0
  35. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/readers/__init__.py +0 -0
  36. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/readers/attachments.py +0 -0
  37. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/readers/excel.py +0 -0
  38. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/readers/json.py +0 -0
  39. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/readers/service.py +0 -0
  40. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/readers/yaml.py +0 -0
  41. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/transformers.py +0 -0
  42. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/utils.py +0 -0
  43. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/writers/__init__.py +0 -0
  44. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/writers/attachment.py +0 -0
  45. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/writers/service.py +0 -0
  46. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/writers/validation_errors.py +0 -0
  47. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/datastreams/writers/yaml.py +0 -0
  48. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/drafts/__init__.py +0 -0
  49. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/drafts/systemfields/__init__.py +0 -0
  50. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/drafts/systemfields/has_draftcheck.py +0 -0
  51. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/expansions/__init__.py +0 -0
  52. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/expansions/expandable_fields.py +0 -0
  53. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/expansions/service.py +0 -0
  54. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/ext.py +0 -0
  55. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/ext_config.py +0 -0
  56. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/facets/__init__.py +0 -0
  57. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/facets/base.py +0 -0
  58. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/facets/date.py +0 -0
  59. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/facets/enum.py +0 -0
  60. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/facets/max_facet.py +0 -0
  61. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/facets/nested_facet.py +0 -0
  62. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/facets/params.py +0 -0
  63. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/i18n/__init__.py +0 -0
  64. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/i18n/default_translations.py +0 -0
  65. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/i18n/dumper.py +0 -0
  66. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/i18n/schema.py +0 -0
  67. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/i18n/ui_schema.py +0 -0
  68. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/i18n/validation.py +0 -0
  69. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/marshmallow.py +0 -0
  70. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/polymorphic.py +0 -0
  71. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/profile.py +0 -0
  72. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/__init__.py +0 -0
  73. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/base.py +0 -0
  74. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/components.py +0 -0
  75. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/internal.py +0 -0
  76. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/lookup.py +0 -0
  77. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/mapping.py +0 -0
  78. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/pid_relation.py +0 -0
  79. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/relations/uow.py +0 -0
  80. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/resolvers/__init__.py +0 -0
  81. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/resolvers/proxies.py +0 -0
  82. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/services/__init__.py +0 -0
  83. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/tasks/__init__.py +0 -0
  84. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/tasks/datastreams.py +0 -0
  85. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/translations/cs/LC_MESSAGES/messages.mo +0 -0
  86. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/translations/cs/LC_MESSAGES/messages.po +0 -0
  87. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/translations/en/LC_MESSAGES/messages.po +0 -0
  88. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/translations/messages.pot +0 -0
  89. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/ui/__init__.py +0 -0
  90. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/ui/marshmallow.py +0 -0
  91. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/uow.py +0 -0
  92. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/utils/__init__.py +0 -0
  93. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/utils/path.py +0 -0
  94. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/validation/__init__.py +0 -0
  95. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime/validation/dates.py +0 -0
  96. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime.egg-info/dependency_links.txt +0 -0
  97. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime.egg-info/entry_points.txt +0 -0
  98. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime.egg-info/requires.txt +0 -0
  99. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/oarepo_runtime.egg-info/top_level.txt +0 -0
  100. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/pyproject.toml +0 -0
  101. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/setup.py +0 -0
  102. {oarepo-runtime-1.4.12 → oarepo-runtime-1.4.13}/tests/pkg_data/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: oarepo-runtime
3
- Version: 1.4.12
3
+ Version: 1.4.13
4
4
  Summary: A set of runtime extensions of Invenio repository
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
@@ -106,71 +106,37 @@ preconfigured permission sets to service config.
106
106
 
107
107
  To use ICU sort and suggestion custom fields, provide the following configuration
108
108
  to `oarepo-model-builder` (or put this stuff to your custom superclasses).
109
- Please rename those RUNTIME_TEST_XXX to your constants
110
109
 
111
110
  ```yaml
112
111
  record:
113
112
  imports:
114
- - import: oarepo_runtime.cf.CustomFields
115
113
  - import: invenio_records_resources.records.api.Record
116
114
  alias: InvenioRecord
117
- - import: invenio_records_resources.records.dumpers.CustomFieldsDumperExt
115
+ - import: oarepo_runtime.records.SystemFieldDumperExt
116
+ - import: oarepo_runtime.records.icu.ICUSortField
117
+ - import: oarepo_runtime.records.icu.ICUSuggestField
118
118
  extra-code: |-2
119
119
  # extra custom fields for testing ICU sorting and suggesting
120
- sort = CustomFields(
121
- "RUNTIME_TEST_SORT_CF",
122
- "sort",
123
- clear_none=True,
124
- create_if_missing=True,
125
- )
126
- suggest = CustomFields(
127
- "RUNTIME_TEST_SUGGEST_CF",
128
- "suggest",
129
- clear_none=True,
130
- create_if_missing=True,
131
- )
120
+ sort = ICUSortField(source_field="metadata.title")
121
+ suggest = ICUSuggestField(source_field="metadata.title")
132
122
  search-options:
133
123
  base-classes:
134
124
  - I18nSearchOptions
135
125
  imports:
136
- - import: oarepo_runtime.services.search.I18nSearchOptions
126
+ - import: oarepo_runtime.services.icu.I18nSearchOptions
127
+ - import: oarepo_runtime.services.icu.ICUSuggestParser
128
+ - import: oarepo_runtime.services.icu.ICUSortOptions
137
129
  sort-options-field: extra_sort_options
138
130
  extra-code: |-2
139
- SORT_CUSTOM_FIELD_NAME = "RUNTIME_TEST_SORT_CF"
140
- SUGGEST_CUSTOM_FIELD_NAME = "RUNTIME_TEST_SUGGEST_CF"
131
+ suggest_parser_cls = ICUSuggestParser("records2")
132
+ sort_options = ICUSortOptions("records2")
141
133
 
142
134
  record-dumper:
143
135
  extensions:
144
- - CustomFieldsDumperExt("RUNTIME_TEST_SORT_CF", "sort")
145
- - CustomFieldsDumperExt("RUNTIME_TEST_SUGGEST_CF", "suggest")
136
+ - SystemFieldDumperExt()
146
137
  ```
147
138
 
148
- Then add the constants to your invenio.cfg
149
-
150
- ```python
151
- from oarepo_runtime.cf.icu import ICUSortCF, ICUSuggestCF
152
-
153
- RUNTIME_TEST_SORT_CF = [
154
- ICUSortCF(
155
- language = "cs",
156
- opensearch_language = "czech",
157
- source_field = "metadata.title",
158
- # cf_name=None, # name of generated custom field, default is <language>
159
- # country=None,
160
- # variant=None,
161
- # sort_option=None, # default is last part of <source_field>
162
- )
163
- ]
164
- RUNTIME_TEST_SUGGEST_CF = [
165
- ICUSuggestCF(
166
- language="cs",
167
- opensearch_language="czech",
168
- source_field="metadata.title",
169
- # cf_name = None # name of generated custom field, default is <language>
170
- )
171
- ]
172
- ```
173
-
174
- Then run `invenio oarepo cf init` to initialize custom fields,
175
- `invenio oarepo index reindex` if you already have data inside the repository
176
- and from this moment on, `/records?sort=title`
139
+ Run `invenio oarepo cf init` to initialize custom fields,
140
+ `invenio oarepo index reindex` if you already have data
141
+ inside the repository and from this moment on,
142
+ `/records?sort=title` and `/records?suggest=abc` should work
@@ -71,71 +71,37 @@ preconfigured permission sets to service config.
71
71
 
72
72
  To use ICU sort and suggestion custom fields, provide the following configuration
73
73
  to `oarepo-model-builder` (or put this stuff to your custom superclasses).
74
- Please rename those RUNTIME_TEST_XXX to your constants
75
74
 
76
75
  ```yaml
77
76
  record:
78
77
  imports:
79
- - import: oarepo_runtime.cf.CustomFields
80
78
  - import: invenio_records_resources.records.api.Record
81
79
  alias: InvenioRecord
82
- - import: invenio_records_resources.records.dumpers.CustomFieldsDumperExt
80
+ - import: oarepo_runtime.records.SystemFieldDumperExt
81
+ - import: oarepo_runtime.records.icu.ICUSortField
82
+ - import: oarepo_runtime.records.icu.ICUSuggestField
83
83
  extra-code: |-2
84
84
  # extra custom fields for testing ICU sorting and suggesting
85
- sort = CustomFields(
86
- "RUNTIME_TEST_SORT_CF",
87
- "sort",
88
- clear_none=True,
89
- create_if_missing=True,
90
- )
91
- suggest = CustomFields(
92
- "RUNTIME_TEST_SUGGEST_CF",
93
- "suggest",
94
- clear_none=True,
95
- create_if_missing=True,
96
- )
85
+ sort = ICUSortField(source_field="metadata.title")
86
+ suggest = ICUSuggestField(source_field="metadata.title")
97
87
  search-options:
98
88
  base-classes:
99
89
  - I18nSearchOptions
100
90
  imports:
101
- - import: oarepo_runtime.services.search.I18nSearchOptions
91
+ - import: oarepo_runtime.services.icu.I18nSearchOptions
92
+ - import: oarepo_runtime.services.icu.ICUSuggestParser
93
+ - import: oarepo_runtime.services.icu.ICUSortOptions
102
94
  sort-options-field: extra_sort_options
103
95
  extra-code: |-2
104
- SORT_CUSTOM_FIELD_NAME = "RUNTIME_TEST_SORT_CF"
105
- SUGGEST_CUSTOM_FIELD_NAME = "RUNTIME_TEST_SUGGEST_CF"
96
+ suggest_parser_cls = ICUSuggestParser("records2")
97
+ sort_options = ICUSortOptions("records2")
106
98
 
107
99
  record-dumper:
108
100
  extensions:
109
- - CustomFieldsDumperExt("RUNTIME_TEST_SORT_CF", "sort")
110
- - CustomFieldsDumperExt("RUNTIME_TEST_SUGGEST_CF", "suggest")
101
+ - SystemFieldDumperExt()
111
102
  ```
112
103
 
113
- Then add the constants to your invenio.cfg
114
-
115
- ```python
116
- from oarepo_runtime.cf.icu import ICUSortCF, ICUSuggestCF
117
-
118
- RUNTIME_TEST_SORT_CF = [
119
- ICUSortCF(
120
- language = "cs",
121
- opensearch_language = "czech",
122
- source_field = "metadata.title",
123
- # cf_name=None, # name of generated custom field, default is <language>
124
- # country=None,
125
- # variant=None,
126
- # sort_option=None, # default is last part of <source_field>
127
- )
128
- ]
129
- RUNTIME_TEST_SUGGEST_CF = [
130
- ICUSuggestCF(
131
- language="cs",
132
- opensearch_language="czech",
133
- source_field="metadata.title",
134
- # cf_name = None # name of generated custom field, default is <language>
135
- )
136
- ]
137
- ```
138
-
139
- Then run `invenio oarepo cf init` to initialize custom fields,
140
- `invenio oarepo index reindex` if you already have data inside the repository
141
- and from this moment on, `/records?sort=title`
104
+ Run `invenio oarepo cf init` to initialize custom fields,
105
+ `invenio oarepo index reindex` if you already have data
106
+ inside the repository and from this moment on,
107
+ `/records?sort=title` and `/records?suggest=abc` should work
@@ -1,15 +1,45 @@
1
+ from typing import List
2
+
1
3
  from flask import current_app
2
4
  from invenio_records.systemfields import DictField, SystemField
5
+ from invenio_records_resources.services.custom_fields import BaseCF
6
+
7
+ from oarepo_runtime.records import MappingSystemFieldMixin
3
8
 
4
9
 
5
- class CustomFieldsMixin:
10
+ class CustomFieldsMixin(MappingSystemFieldMixin):
6
11
  def __init__(self, config_key, *args, **kwargs) -> None:
7
12
  super().__init__(*args, **kwargs)
8
13
  self.config_key = config_key
9
14
 
15
+ @property
16
+ def mapping(self):
17
+ custom_fields: List[BaseCF] = current_app.config[self.config_key]
18
+ return {cf.name: cf.mapping for cf in custom_fields}
19
+
20
+ @property
21
+ def mapping_settings(self):
22
+ return {}
23
+
24
+ def search_dump(self, data):
25
+ custom_fields = current_app.config.get(self.config_key, {})
26
+
27
+ for cf in custom_fields:
28
+ cf.dump(data, cf_key=self.key)
29
+ return data
30
+
31
+ def search_load(self, data):
32
+ custom_fields = current_app.config.get(self.config_key, {})
33
+
34
+ for cf in custom_fields:
35
+ cf.load(data, cf_key=self.key)
36
+ return data
37
+
10
38
 
11
39
  class CustomFields(CustomFieldsMixin, DictField):
12
- pass
40
+ @property
41
+ def mapping(self):
42
+ return {self.key: {"type": "object", "properties": super().mapping}}
13
43
 
14
44
 
15
45
  class InlinedCustomFields(CustomFieldsMixin, SystemField):
@@ -12,15 +12,6 @@ def cf():
12
12
 
13
13
 
14
14
  @cf.command(name="init", help="Prepare custom fields in indices")
15
- @click.option(
16
- "-f",
17
- "--field-name",
18
- "field_names",
19
- type=str,
20
- required=False,
21
- multiple=True,
22
- help="A custom field name to create. If not provided, all custom fields will be created.",
23
- )
24
15
  @with_appcontext
25
- def init(field_names):
26
- prepare_cf_indices(field_names)
16
+ def init():
17
+ prepare_cf_indices()
@@ -2,23 +2,18 @@ import inspect
2
2
  from typing import Iterable, List
3
3
 
4
4
  import click
5
- from flask import current_app
5
+ import deepmerge
6
6
  from invenio_records_resources.proxies import current_service_registry
7
- from invenio_records_resources.services.custom_fields import BaseCF
8
7
  from invenio_records_resources.services.custom_fields.mappings import (
9
8
  Mapping as InvenioMapping,
10
9
  )
11
- from invenio_records_resources.services.custom_fields.validate import (
12
- validate_custom_fields,
13
- )
14
10
  from invenio_records_resources.services.records.config import RecordServiceConfig
15
11
  from invenio_records_resources.services.records.service import RecordService
16
12
  from invenio_search import current_search_client
17
13
  from invenio_search.engine import dsl, search
18
14
  from invenio_search.utils import build_alias_name
19
15
 
20
- from oarepo_runtime.cf import CustomFieldsMixin
21
- import deepmerge
16
+ from oarepo_runtime.records import MappingSystemFieldMixin
22
17
 
23
18
 
24
19
  class Mapping(InvenioMapping):
@@ -69,34 +64,22 @@ class Mapping(InvenioMapping):
69
64
 
70
65
  # pieces taken from https://github.com/inveniosoftware/invenio-rdm-records/blob/master/invenio_rdm_records/cli.py
71
66
  # as cf initialization is not supported directly in plain invenio
72
- def prepare_cf_indices(field_names: List[str] = None):
67
+ def prepare_cf_indices():
73
68
  service: RecordService
74
69
  for service in current_service_registry._services.values():
75
70
  config: RecordServiceConfig = service.config
76
- prepare_cf_index(config, field_names)
71
+ prepare_cf_index(config)
77
72
 
78
73
 
79
- def prepare_cf_index(config: RecordServiceConfig, field_names: List[str] = None):
74
+ def prepare_cf_index(config: RecordServiceConfig):
80
75
  record_class = getattr(config, "record_cls", None)
81
76
  if not record_class:
82
77
  return
83
78
 
84
- # try to get custom fields from the record class
85
- # validate them
86
- for field_name, available_fields in get_custom_fields(record_class):
87
- validate_custom_fields(
88
- given_fields=field_names, available_fields=available_fields, namespaces=[]
89
- )
90
-
79
+ for fld in get_mapping_fields(record_class):
91
80
  # get mapping
92
- properties = Mapping.properties_for_fields(
93
- field_names, available_fields, field_name=field_name
94
- )
95
- settings = Mapping.settings_for_fields(
96
- field_names, available_fields, field_name=field_name
97
- )
98
- if not properties:
99
- continue
81
+ mapping = fld.mapping
82
+ settings = fld.mapping_settings
100
83
 
101
84
  # upload mapping
102
85
  try:
@@ -106,12 +89,7 @@ def prepare_cf_index(config: RecordServiceConfig, field_names: List[str] = None)
106
89
  ),
107
90
  using=current_search_client,
108
91
  )
109
- if settings:
110
- record_index.close()
111
- record_index.put_settings(body=settings)
112
- record_index.open()
113
- if properties:
114
- record_index.put_mapping(body={"properties": properties})
92
+ update_index(record_index, settings, mapping)
115
93
 
116
94
  if hasattr(config, "draft_cls"):
117
95
  draft_index = dsl.Index(
@@ -120,15 +98,24 @@ def prepare_cf_index(config: RecordServiceConfig, field_names: List[str] = None)
120
98
  ),
121
99
  using=current_search_client,
122
100
  )
123
- draft_index.put_mapping(body={"properties": properties})
101
+ update_index(draft_index, settings, mapping)
124
102
 
125
103
  except search.RequestError as e:
126
104
  click.secho("An error occurred while creating custom fields.", fg="red")
127
105
  click.secho(e.info["error"]["reason"], fg="red")
128
106
 
129
107
 
130
- def get_custom_fields(record_class) -> Iterable[List[BaseCF]]:
108
+ def update_index(record_index, settings, mapping):
109
+ if settings:
110
+ record_index.close()
111
+ record_index.put_settings(body=settings)
112
+ record_index.open()
113
+ if mapping:
114
+ record_index.put_mapping(body={"properties": mapping})
115
+
116
+
117
+ def get_mapping_fields(record_class) -> Iterable[MappingSystemFieldMixin]:
131
118
  for cfg_name, cfg_value in inspect.getmembers(
132
- record_class, lambda x: isinstance(x, CustomFieldsMixin)
119
+ record_class, lambda x: isinstance(x, MappingSystemFieldMixin)
133
120
  ):
134
- yield cfg_value._key, current_app.config[cfg_value.config_key]
121
+ yield cfg_value
@@ -1,4 +1,3 @@
1
- import itertools
2
1
  import sys
3
2
 
4
3
  import click
@@ -0,0 +1,29 @@
1
+ import inspect
2
+
3
+ from invenio_records.dumpers import SearchDumperExt
4
+
5
+
6
+ class MappingSystemFieldMixin:
7
+ @property
8
+ def mapping(self):
9
+ return {}
10
+
11
+ @property
12
+ def mapping_settings(self):
13
+ return {}
14
+
15
+
16
+ class SystemFieldDumperExt(SearchDumperExt):
17
+ def dump(self, record, data):
18
+ """Dump custom fields."""
19
+ for cf in inspect.getmembers(
20
+ record, lambda x: isinstance(x, MappingSystemFieldMixin)
21
+ ):
22
+ cf[1].search_dump(data)
23
+
24
+ def load(self, data, record_cls):
25
+ """Load custom fields."""
26
+ for cf in inspect.getmembers(
27
+ record_cls, lambda x: isinstance(x, MappingSystemFieldMixin)
28
+ ):
29
+ cf[1].search_load(data)
@@ -0,0 +1,132 @@
1
+ from functools import cached_property
2
+ from typing import Dict
3
+
4
+ from flask import current_app
5
+ from invenio_records.systemfields import SystemField
6
+
7
+ from oarepo_runtime.records import MappingSystemFieldMixin
8
+ from oarepo_runtime.relations.lookup import lookup_key
9
+
10
+
11
+ class ICUField(SystemField):
12
+ """
13
+ A system field that acts as an opensearch "proxy" to another field.
14
+ It creates a top-level mapping field with the same name and copies
15
+ content of {another field}.language into {mapping field}.language.
16
+
17
+ The language accessor can be modified by overriding get_values method.
18
+ """
19
+
20
+ def __init__(self, *, source_field, key=None):
21
+ super().__init__(key)
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
+ ret.append(l.value)
41
+ elif isinstance(l.value, dict):
42
+ val = l.value.get(language)
43
+ if val:
44
+ ret.append(val)
45
+ return ret
46
+
47
+ def search_dump(self, data):
48
+ ret = {}
49
+ for lang in self.languages:
50
+ ret[lang] = self.get_values(data, lang)
51
+ data[self.attr_name] = ret
52
+
53
+ def search_load(self, data):
54
+ data.pop(self.attr_name, None)
55
+
56
+ def __get__(self, instance, owner):
57
+ return self
58
+
59
+
60
+ class ICUSortField(MappingSystemFieldMixin, ICUField):
61
+ """
62
+ A field that adds icu sorting field
63
+ """
64
+
65
+ def __init__(self, *, source_field, key=None):
66
+ super().__init__(source_field=source_field, key=key)
67
+
68
+ @property
69
+ def mapping(self):
70
+ return {
71
+ self.attr_name: {
72
+ "type": "object",
73
+ "properties": {
74
+ lang: {
75
+ "type": "icu_collation_keyword",
76
+ "index": False,
77
+ "language": lang,
78
+ **setting.get("collation", {}),
79
+ }
80
+ for lang, setting in self.languages.items()
81
+ },
82
+ },
83
+ }
84
+
85
+
86
+ class ICUSuggestField(MappingSystemFieldMixin, ICUField):
87
+ """
88
+ A field that adds icu-aware suggestion field
89
+ """
90
+
91
+ def __init__(self, source_field, key=None):
92
+ super().__init__(source_field=source_field, key=key)
93
+
94
+ @property
95
+ def mapping(self):
96
+ return {
97
+ self.attr_name: {
98
+ "type": "object",
99
+ "properties": {
100
+ lang: setting.get(
101
+ "suggest",
102
+ {
103
+ "type": "text",
104
+ "fields": {
105
+ "original": {
106
+ "type": "search_as_you_type",
107
+ },
108
+ "no_accent": {
109
+ "type": "search_as_you_type",
110
+ "analyzer": "accent_removal_analyzer",
111
+ },
112
+ },
113
+ },
114
+ )
115
+ for lang, setting in self.languages.items()
116
+ },
117
+ },
118
+ }
119
+
120
+ @property
121
+ def mapping_settings(self):
122
+ return {
123
+ "analysis": {
124
+ "analyzer": {
125
+ "accent_removal_analyzer": {
126
+ "type": "custom",
127
+ "tokenizer": "standard",
128
+ "filter": ["lowercase", "asciifolding"],
129
+ }
130
+ }
131
+ }
132
+ }
@@ -12,5 +12,7 @@ class MultipleInvalidRelationErrors(Exception):
12
12
  """
13
13
 
14
14
  def __init__(self, errors):
15
- super().__init__("; ".join([f"{e[0]}: {type(e[1]).__name__}({e[1]})" for e in errors]))
15
+ super().__init__(
16
+ "; ".join([f"{e[0]}: {type(e[1]).__name__}({e[1]})" for e in errors])
17
+ )
16
18
  self.errors = errors