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.
Files changed (171) hide show
  1. oarepo_runtime/__init__.py +24 -0
  2. oarepo_runtime/api.py +210 -0
  3. oarepo_runtime/cli/__init__.py +10 -21
  4. oarepo_runtime/cli/search.py +34 -0
  5. oarepo_runtime/config.py +98 -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 +61 -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/facets/__init__.py +12 -33
  20. oarepo_runtime/services/facets/params.py +45 -110
  21. oarepo_runtime/services/records/__init__.py +14 -1
  22. oarepo_runtime/services/records/links.py +21 -11
  23. oarepo_runtime/services/records/mapping.py +42 -0
  24. oarepo_runtime/services/results.py +98 -109
  25. oarepo_runtime/services/schema/__init__.py +12 -44
  26. oarepo_runtime/services/schema/i18n.py +47 -22
  27. oarepo_runtime/services/schema/i18n_ui.py +61 -24
  28. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev4.dist-info}/METADATA +10 -21
  29. oarepo_runtime-2.0.0.dev4.dist-info/RECORD +32 -0
  30. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev4.dist-info}/WHEEL +1 -2
  31. oarepo_runtime-2.0.0.dev4.dist-info/entry_points.txt +5 -0
  32. oarepo_runtime/cli/assets.py +0 -145
  33. oarepo_runtime/cli/base.py +0 -25
  34. oarepo_runtime/cli/cf.py +0 -15
  35. oarepo_runtime/cli/check.py +0 -167
  36. oarepo_runtime/cli/configuration.py +0 -51
  37. oarepo_runtime/cli/fixtures.py +0 -167
  38. oarepo_runtime/cli/index.py +0 -272
  39. oarepo_runtime/cli/permissions/__init__.py +0 -6
  40. oarepo_runtime/cli/permissions/base.py +0 -26
  41. oarepo_runtime/cli/permissions/evaluate.py +0 -63
  42. oarepo_runtime/cli/permissions/list.py +0 -239
  43. oarepo_runtime/cli/permissions/search.py +0 -121
  44. oarepo_runtime/cli/validate.py +0 -150
  45. oarepo_runtime/datastreams/__init__.py +0 -38
  46. oarepo_runtime/datastreams/asynchronous.py +0 -247
  47. oarepo_runtime/datastreams/catalogue.py +0 -150
  48. oarepo_runtime/datastreams/datastreams.py +0 -152
  49. oarepo_runtime/datastreams/errors.py +0 -54
  50. oarepo_runtime/datastreams/ext.py +0 -41
  51. oarepo_runtime/datastreams/fixtures.py +0 -265
  52. oarepo_runtime/datastreams/json.py +0 -4
  53. oarepo_runtime/datastreams/readers/__init__.py +0 -39
  54. oarepo_runtime/datastreams/readers/attachments.py +0 -51
  55. oarepo_runtime/datastreams/readers/excel.py +0 -123
  56. oarepo_runtime/datastreams/readers/json.py +0 -27
  57. oarepo_runtime/datastreams/readers/service.py +0 -54
  58. oarepo_runtime/datastreams/readers/yaml.py +0 -14
  59. oarepo_runtime/datastreams/semi_asynchronous.py +0 -91
  60. oarepo_runtime/datastreams/synchronous.py +0 -70
  61. oarepo_runtime/datastreams/transformers.py +0 -18
  62. oarepo_runtime/datastreams/types.py +0 -323
  63. oarepo_runtime/datastreams/utils.py +0 -131
  64. oarepo_runtime/datastreams/writers/__init__.py +0 -21
  65. oarepo_runtime/datastreams/writers/attachments_file.py +0 -92
  66. oarepo_runtime/datastreams/writers/attachments_service.py +0 -118
  67. oarepo_runtime/datastreams/writers/publish.py +0 -70
  68. oarepo_runtime/datastreams/writers/service.py +0 -175
  69. oarepo_runtime/datastreams/writers/utils.py +0 -30
  70. oarepo_runtime/datastreams/writers/validation_errors.py +0 -20
  71. oarepo_runtime/datastreams/writers/yaml.py +0 -56
  72. oarepo_runtime/ext_config.py +0 -67
  73. oarepo_runtime/i18n/__init__.py +0 -3
  74. oarepo_runtime/info/__init__.py +0 -0
  75. oarepo_runtime/info/check.py +0 -95
  76. oarepo_runtime/info/permissions/__init__.py +0 -0
  77. oarepo_runtime/info/permissions/debug.py +0 -191
  78. oarepo_runtime/info/views.py +0 -586
  79. oarepo_runtime/profile.py +0 -60
  80. oarepo_runtime/records/dumpers/__init__.py +0 -8
  81. oarepo_runtime/records/dumpers/edtf_interval.py +0 -38
  82. oarepo_runtime/records/dumpers/multilingual_dumper.py +0 -34
  83. oarepo_runtime/records/entity_resolvers/__init__.py +0 -13
  84. oarepo_runtime/records/entity_resolvers/proxies.py +0 -57
  85. oarepo_runtime/records/mappings/__init__.py +0 -0
  86. oarepo_runtime/records/mappings/rdm_parent_mapping.json +0 -483
  87. oarepo_runtime/records/owners/__init__.py +0 -3
  88. oarepo_runtime/records/owners/registry.py +0 -22
  89. oarepo_runtime/records/relations/__init__.py +0 -22
  90. oarepo_runtime/records/relations/base.py +0 -296
  91. oarepo_runtime/records/relations/internal.py +0 -46
  92. oarepo_runtime/records/relations/lookup.py +0 -28
  93. oarepo_runtime/records/relations/pid_relation.py +0 -102
  94. oarepo_runtime/records/systemfields/featured_file.py +0 -45
  95. oarepo_runtime/records/systemfields/has_draftcheck.py +0 -47
  96. oarepo_runtime/records/systemfields/icu.py +0 -371
  97. oarepo_runtime/records/systemfields/owner.py +0 -115
  98. oarepo_runtime/records/systemfields/record_status.py +0 -35
  99. oarepo_runtime/records/systemfields/selectors.py +0 -98
  100. oarepo_runtime/records/systemfields/synthetic.py +0 -130
  101. oarepo_runtime/resources/__init__.py +0 -4
  102. oarepo_runtime/resources/config.py +0 -12
  103. oarepo_runtime/resources/file_resource.py +0 -15
  104. oarepo_runtime/resources/json_serializer.py +0 -27
  105. oarepo_runtime/resources/localized_ui_json_serializer.py +0 -54
  106. oarepo_runtime/resources/resource.py +0 -53
  107. oarepo_runtime/resources/responses.py +0 -20
  108. oarepo_runtime/services/components.py +0 -429
  109. oarepo_runtime/services/config/draft_link.py +0 -23
  110. oarepo_runtime/services/config/permissions_presets.py +0 -174
  111. oarepo_runtime/services/config/service.py +0 -117
  112. oarepo_runtime/services/custom_fields/__init__.py +0 -80
  113. oarepo_runtime/services/custom_fields/mappings.py +0 -188
  114. oarepo_runtime/services/entity/__init__.py +0 -0
  115. oarepo_runtime/services/entity/config.py +0 -14
  116. oarepo_runtime/services/entity/schema.py +0 -9
  117. oarepo_runtime/services/entity/service.py +0 -48
  118. oarepo_runtime/services/expansions/__init__.py +0 -0
  119. oarepo_runtime/services/expansions/expandable_fields.py +0 -21
  120. oarepo_runtime/services/expansions/service.py +0 -4
  121. oarepo_runtime/services/facets/base.py +0 -12
  122. oarepo_runtime/services/facets/date.py +0 -72
  123. oarepo_runtime/services/facets/enum.py +0 -11
  124. oarepo_runtime/services/facets/facet_groups_names.py +0 -17
  125. oarepo_runtime/services/facets/max_facet.py +0 -13
  126. oarepo_runtime/services/facets/multilingual_facet.py +0 -33
  127. oarepo_runtime/services/facets/nested_facet.py +0 -32
  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.dev4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,5 @@
1
+ [invenio_base.api_apps]
2
+ oarepo_runtime = oarepo_runtime.ext:OARepoRuntime
3
+
4
+ [invenio_base.apps]
5
+ oarepo_runtime = oarepo_runtime.ext:OARepoRuntime
@@ -1,145 +0,0 @@
1
- import json
2
- import os
3
- import re
4
- from pathlib import Path
5
-
6
- import click
7
- from flask import current_app
8
- from flask.cli import with_appcontext
9
- from flask_webpackext import current_webpack
10
- from importlib_metadata import entry_points
11
-
12
- from .base import oarepo
13
-
14
-
15
- @oarepo.group()
16
- def assets():
17
- "OARepo asset addons"
18
-
19
-
20
- @assets.command()
21
- @click.argument("output_file")
22
- @click.option("--repository-dir")
23
- @click.option("--assets-dir", default=".assets")
24
- @with_appcontext
25
- def collect(output_file, repository_dir, assets_dir):
26
- aliases, asset_dirs, generated_paths = enumerate_assets()
27
-
28
- app_and_blueprints = [current_app] + list(current_app.blueprints.values())
29
-
30
- static_deps = []
31
- instance_path = current_app.instance_path
32
- if instance_path[-1] != "/":
33
- instance_path += "/"
34
-
35
- for bp in app_and_blueprints:
36
- if (
37
- bp.has_static_folder
38
- and os.path.isdir(bp.static_folder)
39
- and not bp.static_folder.startswith(instance_path)
40
- ):
41
- static_deps.append(bp.static_folder)
42
-
43
- root_aliases = {}
44
- asset_paths = [Path(x) for x in asset_dirs]
45
- for alias, path in aliases.items():
46
- for pth in asset_paths:
47
- possible_path = pth / path
48
- if possible_path.exists():
49
- try:
50
- relative_path = str(
51
- possible_path.relative_to(repository_dir or os.getcwd())
52
- )
53
- root_aliases[alias] = "./" + relative_path
54
- except ValueError:
55
- root_aliases[alias] = str(Path(assets_dir) / path)
56
-
57
- with open(output_file, "w") as f:
58
- json.dump(
59
- {
60
- "assets": asset_dirs,
61
- "static": static_deps,
62
- "@aliases": aliases,
63
- "@root_aliases": root_aliases,
64
- "generated": generated_paths,
65
- },
66
- f,
67
- indent=4,
68
- ensure_ascii=False,
69
- )
70
-
71
-
72
- def enumerate_assets():
73
- asset_dirs = []
74
- generated_paths = []
75
- aliases = {}
76
- themes = current_app.config["APP_THEME"] or ["semantic-ui"]
77
- project = current_webpack.project
78
- if hasattr(project, 'generated_paths'):
79
- generated_paths += project.generated_paths
80
-
81
- for ep in entry_points(group="invenio_assets.webpack"):
82
- webpack = ep.load()
83
- for wp_theme_name, wp_theme in webpack.themes.items():
84
- if wp_theme_name in themes:
85
- asset_dirs.append(wp_theme.path)
86
- if hasattr(wp_theme, "generated_paths"):
87
- generated_paths += list(set(wp_theme.generated_paths) - set(generated_paths))
88
- aliases.update(wp_theme.aliases)
89
- return aliases, asset_dirs, generated_paths
90
-
91
-
92
- COMPONENT_LIST_RE = re.compile(
93
- r"""
94
- ^
95
- \s*
96
- & # start of import statement & { import "blah"; }
97
- \s*
98
- {
99
- \s*
100
- (
101
- @import\s+["'](.*?)["']
102
- \s*
103
- ;
104
- )+
105
- \s*
106
- }""",
107
- re.MULTILINE | re.DOTALL | re.VERBOSE,
108
- )
109
-
110
- COMPONENT_RE = re.compile(
111
- r"""
112
- \s*
113
- @import\s+["'](.*?)["']
114
- \s*
115
- ;
116
- \s*
117
- """,
118
- re.MULTILINE | re.DOTALL | re.VERBOSE,
119
- )
120
-
121
-
122
-
123
- @assets.command(name="less-components")
124
- @click.argument("output_file", default="-")
125
- @with_appcontext
126
- def less_components(output_file):
127
- aliases, asset_dirs, _ = enumerate_assets()
128
- asset_dirs = [Path(x) for x in asset_dirs]
129
- less_component_files = []
130
- for asset_dir in asset_dirs:
131
- less_dir = asset_dir / "less"
132
- if less_dir.exists():
133
- for f in less_dir.glob("**/custom-components.less"):
134
- less_component_files.append(f)
135
- components = set()
136
- for cmp_file in less_component_files:
137
- for component_list in COMPONENT_LIST_RE.findall(cmp_file.read_text()):
138
- for s in COMPONENT_RE.findall(component_list[0]):
139
- components.add(Path(s).stem)
140
- data = {"components": list(sorted(components))}
141
- if output_file == "-":
142
- print(json.dumps(data, indent=4, ensure_ascii=False))
143
- else:
144
- with open(output_file, "w") as f:
145
- json.dump(data, f, indent=4, ensure_ascii=False)
@@ -1,25 +0,0 @@
1
- import json
2
-
3
- import click
4
- import importlib_metadata
5
-
6
-
7
- @click.group()
8
- def oarepo():
9
- """OARepo commands."""
10
-
11
-
12
- def as_command(group, name, *args):
13
- args = [group.command(name=name), *args]
14
- actual = args[-1]
15
- for arg in reversed(args[:-1]):
16
- actual = arg(actual)
17
- return actual
18
-
19
-
20
- @oarepo.command(name="version")
21
- def get_version():
22
- versions = {}
23
- for distro in importlib_metadata.distributions():
24
- versions[distro.metadata["Name"]] = distro.version
25
- print(json.dumps(versions, ensure_ascii=False, indent=4))
oarepo_runtime/cli/cf.py DELETED
@@ -1,15 +0,0 @@
1
- from flask.cli import with_appcontext
2
-
3
- from oarepo_runtime.cli import oarepo
4
- from oarepo_runtime.services.custom_fields.mappings import prepare_cf_indices
5
-
6
-
7
- @oarepo.group()
8
- def cf():
9
- """Custom fields commands."""
10
-
11
-
12
- @cf.command(name="init", help="Prepare custom fields in indices")
13
- @with_appcontext
14
- def init():
15
- prepare_cf_indices()
@@ -1,167 +0,0 @@
1
- import json
2
- import random
3
- import traceback
4
-
5
- import click
6
- import kombu.exceptions
7
- import opensearchpy
8
- import redis
9
- from flask import current_app
10
- from flask.cli import with_appcontext
11
- from invenio_db import db
12
- from invenio_files_rest.models import Location
13
- from invenio_pidstore.models import PersistentIdentifier
14
- from invenio_records_resources.proxies import current_service_registry
15
- from opensearchpy import TransportError
16
-
17
- from .base import oarepo
18
-
19
-
20
- @oarepo.command(name="check")
21
- @click.argument("output_file", default="-")
22
- @with_appcontext
23
- def check(output_file):
24
- status = {}
25
- status["db"] = check_database()
26
- status["opensearch"] = check_opensearch()
27
- status["files"] = check_files()
28
- status["mq"] = check_message_queue()
29
- status["cache"] = check_cache()
30
- if output_file == "-":
31
- print(
32
- json.dumps(status, indent=4, ensure_ascii=False, default=lambda x: str(x))
33
- )
34
- else:
35
- with open(output_file, "w") as f:
36
- json.dump(status, f, ensure_ascii=False, default=lambda x: str(x))
37
-
38
-
39
- def check_database():
40
- if not has_database_connection():
41
- return "connection_error"
42
- try:
43
- db.session.begin()
44
- try:
45
- PersistentIdentifier.query.all()[:1]
46
- except:
47
- return "not_initialized"
48
- alembic = current_app.extensions["invenio-db"].alembic
49
- context = alembic.migration_context
50
- db_heads = set(context.get_current_heads())
51
- source_heads = [x.revision for x in alembic.current()]
52
- for h in source_heads:
53
- if h not in db_heads:
54
- return "migration_pending"
55
- return "ok"
56
- finally:
57
- db.session.rollback()
58
-
59
-
60
- def has_database_connection():
61
- try:
62
- db.session.begin()
63
- db.session.execute("SELECT 1")
64
- return True
65
- except:
66
- return False
67
- finally:
68
- db.session.rollback()
69
-
70
-
71
- def check_opensearch():
72
- services = current_service_registry._services.keys()
73
- checked_indexers = set()
74
- for service_id in services:
75
- service = current_service_registry.get(service_id)
76
- record_class = getattr(service.config, "record_cls", None)
77
- if not record_class: # files??
78
- continue
79
- indexer = getattr(service, "indexer", None)
80
- if not indexer:
81
- continue
82
- if id(indexer) not in checked_indexers:
83
- checked_indexers.add(id(indexer))
84
- try:
85
- indexer.client.indices.exists("test")
86
- except opensearchpy.exceptions.ConnectionError:
87
- return "connection_error"
88
-
89
-
90
- try:
91
- index = indexer._prepare_index(indexer.record_to_index(record_class))
92
- except AttributeError:
93
- print(f"Warning: can not get index name for record class {record_class}")
94
- continue
95
-
96
- try:
97
- service.indexer.client.indices.get(index=index)
98
- except TransportError:
99
- return f"index_missing:{index}"
100
- return "ok"
101
-
102
-
103
- def check_files():
104
- if not has_database_connection():
105
- return "db_connection_error"
106
-
107
- try:
108
- db.session.begin()
109
- # check that there is the default location and that is readable
110
- default_location = Location.get_default()
111
- if not default_location:
112
- return "default_location_missing"
113
- except: # NOSONAR - we are not interested what the exception is
114
- return "db_error"
115
- finally:
116
- db.session.rollback()
117
-
118
- try:
119
- import s3fs
120
- except ImportError:
121
- return f"s3_support_not_installed"
122
-
123
- try:
124
- info = current_app.extensions["invenio-s3"].init_s3fs_info
125
- fs = s3fs.S3FileSystem(default_block_size=4096, **info)
126
- fs.ls(default_location.uri.replace("s3://", ""))
127
- except: # NOSONAR - we are not interested what the exception is
128
- return f"bucket_does_not_exist:{default_location.uri}"
129
-
130
- return "ok"
131
-
132
-
133
- def check_message_queue():
134
- try:
135
- from celery import current_app
136
-
137
- current_app.control.inspect().active()
138
- return "ok"
139
- except kombu.exceptions.OperationalError as e:
140
- if isinstance(e.__cause__, ConnectionRefusedError):
141
- return "connection_error"
142
- return "mq_error"
143
- except: # NOSONAR - we are not interested what the exception is
144
- return "mq_error"
145
-
146
-
147
- def check_cache():
148
- try:
149
- from invenio_cache.proxies import current_cache
150
-
151
- rnd = str(
152
- random.randint(0, 10000) # NOSONAR - this is not a cryptographic random
153
- )
154
- # it is here just to make sure that what we put to the cache is what we get back
155
-
156
- current_cache.set("oarepo_check", rnd)
157
- if current_cache.get("oarepo_check") == rnd:
158
- return "ok"
159
- else:
160
- return "cache_error"
161
- except redis.exceptions.ConnectionError as e:
162
- if isinstance(e.__cause__, ConnectionRefusedError):
163
- return "connection_error"
164
- return "cache_exception"
165
- except: # NOSONAR - we are not interested what the exception is
166
- traceback.print_exc()
167
- return "cache_exception"
@@ -1,51 +0,0 @@
1
- import json
2
- from collections.abc import Mapping, Sequence, Set
3
-
4
- import click
5
- from flask import current_app
6
- from flask.cli import with_appcontext
7
- from werkzeug.local import LocalProxy
8
-
9
- from .base import oarepo
10
-
11
- def remove_lazy_objects(obj):
12
- if isinstance(obj, Sequence):
13
- if isinstance(obj, list):
14
- return [remove_lazy_objects(item) for item in obj if not isinstance(item, LocalProxy)]
15
- elif isinstance(obj, tuple):
16
- return tuple(remove_lazy_objects(item) for item in obj if not isinstance(item, LocalProxy))
17
- elif not isinstance(obj, LocalProxy):
18
- return obj # strings, bytes, bytesarray etc.
19
- elif isinstance(obj, Set):
20
- if isinstance(obj, frozenset):
21
- return frozenset(remove_lazy_objects(item) for item in obj if not isinstance(item, LocalProxy))
22
- return {remove_lazy_objects(item) for item in obj if not isinstance(item, LocalProxy)}
23
- elif isinstance(obj, Mapping):
24
- return {k: remove_lazy_objects(v) for k, v in obj.items() if not isinstance(v, LocalProxy)}
25
- elif not isinstance(obj, LocalProxy):
26
- return obj # everything else that is not localproxy
27
-
28
- @oarepo.command(name="configuration")
29
- @click.argument("output_file", default="-")
30
- @with_appcontext
31
- def configuration_command(output_file):
32
- configuration = remove_lazy_objects(current_app.config)
33
-
34
- try:
35
- invenio_db = current_app.extensions["invenio-db"]
36
- alembic_config = invenio_db.alembic.config
37
- configuration["ALEMBIC_LOCATIONS"] = alembic_config.get_main_option(
38
- "version_locations"
39
- ).split(",")
40
- except Exception as e:
41
- configuration["ALEMBIC_LOCATIONS_ERROR"] = str(e)
42
-
43
- if output_file == "-":
44
- print(
45
- json.dumps(
46
- configuration, skipkeys=True, indent=4, ensure_ascii=False, default=lambda x: str(x)
47
- )
48
- )
49
- else:
50
- with open(output_file, "w") as f:
51
- json.dump(configuration, f,skipkeys=True, ensure_ascii=False, default=lambda x: str(x))
@@ -1,167 +0,0 @@
1
- import click
2
- import tqdm
3
- from flask import current_app
4
- from flask.cli import with_appcontext
5
- from flask_principal import Identity, RoleNeed, UserNeed
6
- from invenio_access.permissions import any_user, authenticated_user, system_identity
7
- from invenio_accounts.models import User
8
-
9
- from oarepo_runtime.cli import oarepo
10
- from oarepo_runtime.datastreams import SynchronousDataStream
11
- from oarepo_runtime.datastreams.asynchronous import AsynchronousDataStream
12
- from oarepo_runtime.datastreams.fixtures import (
13
- FixturesCallback,
14
- dump_fixtures,
15
- fixtures_asynchronous_callback,
16
- load_fixtures,
17
- )
18
- from oarepo_runtime.datastreams.types import StatsKeepingDataStreamCallback
19
-
20
-
21
- @oarepo.group()
22
- def fixtures():
23
- """Load and dump fixtures"""
24
-
25
-
26
- @fixtures.command()
27
- @click.argument("fixture_dir_or_catalogue", required=False)
28
- @click.option("--include", multiple=True)
29
- @click.option("--exclude", multiple=True)
30
- @click.option("--system-fixtures/--no-system-fixtures", default=True, is_flag=True)
31
- @click.option("--verbose", is_flag=True)
32
- @click.option("--on-background", is_flag=True)
33
- @click.option(
34
- "--bulk-size",
35
- default=100,
36
- type=int,
37
- help="Size for bulk indexing - this number of records "
38
- "will be committed in a single transaction and indexed together",
39
- )
40
- @click.option("--batch-size", help="Alias for --bulk-size", type=int)
41
- @click.option(
42
- "--identity", help="Email of the identity that will be used to import the data"
43
- )
44
- @with_appcontext
45
- def load(
46
- fixture_dir_or_catalogue=None,
47
- include=None,
48
- exclude=None,
49
- system_fixtures=None,
50
- verbose=False,
51
- bulk_size=100,
52
- on_background=False,
53
- batch_size=None,
54
- identity=None,
55
- ):
56
- """Loads fixtures"""
57
- if batch_size:
58
- bulk_size = batch_size
59
- if not on_background:
60
- callback = TQDMCallback(verbose=verbose)
61
- else:
62
- callback = fixtures_asynchronous_callback.s()
63
-
64
- if fixture_dir_or_catalogue:
65
- system_fixtures = False
66
-
67
- if not identity:
68
- user = None
69
- identity = system_identity
70
- else:
71
- # identity is user email
72
- user = User.query.filter_by(email=identity).one()
73
- identity = Identity(user.id)
74
-
75
- # TODO: add provides. How to do it better? It seems that we can not use
76
- # flask signals to add these, as they depend on request context that is
77
- # not available here
78
- identity.provides.add(any_user)
79
- identity.provides.add(authenticated_user)
80
- identity.provides.add(UserNeed(user.id))
81
- for role in getattr(user, "roles", []):
82
- identity.provides.add(RoleNeed(role.name))
83
- # TODO: community roles ...
84
-
85
- with current_app.wsgi_app.mounts["/api"].app_context():
86
- load_fixtures(
87
- fixture_dir_or_catalogue,
88
- _make_list(include),
89
- _make_list(exclude),
90
- system_fixtures=system_fixtures,
91
- callback=callback,
92
- batch_size=bulk_size,
93
- datastreams_impl=(
94
- AsynchronousDataStream if on_background else SynchronousDataStream
95
- ),
96
- identity=identity,
97
- )
98
- if not on_background:
99
- _show_stats(callback, "Load fixtures")
100
-
101
-
102
- @fixtures.command()
103
- @click.option("--include", multiple=True)
104
- @click.option("--exclude", multiple=True)
105
- @click.argument("fixture_dir", required=True)
106
- @click.option("--verbose", is_flag=True)
107
- @with_appcontext
108
- def dump(fixture_dir, include, exclude, verbose):
109
- """Dump fixtures"""
110
- callback = TQDMCallback(verbose=verbose)
111
-
112
- with current_app.wsgi_app.mounts["/api"].app_context():
113
- dump_fixtures(
114
- fixture_dir,
115
- _make_list(include),
116
- _make_list(exclude),
117
- callback=TQDMCallback(verbose=verbose),
118
- )
119
- _show_stats(callback, "Dump fixtures")
120
-
121
-
122
- def _make_list(lst):
123
- return [
124
- item.strip() for lst_item in lst for item in lst_item.split(",") if item.strip()
125
- ]
126
-
127
-
128
- def _show_stats(callback: StatsKeepingDataStreamCallback, title: str):
129
- print("\n\n")
130
- print(f"{title} stats:")
131
- print(callback.stats())
132
-
133
-
134
- class TQDMCallback(FixturesCallback):
135
- def __init__(self, message_prefix="Loading ", verbose=False):
136
- super().__init__()
137
- self._tqdm = tqdm.tqdm(unit=" item(s)")
138
- self._message_prefix = message_prefix
139
- self._verbose = verbose
140
-
141
- def fixture_started(self, fixture_name):
142
- self._tqdm.set_description(f"{self._message_prefix}{fixture_name} running")
143
-
144
- def fixture_finished(self, fixture_name):
145
- self._tqdm.set_description(f"{self._message_prefix}{fixture_name} finished")
146
-
147
- def batch_finished(self, batch):
148
- super().batch_finished(batch)
149
- self._tqdm.update(len(batch.entries))
150
- for err in batch.errors:
151
- self._tqdm.write("Failed batch: {}: {}".format(err, batch))
152
- if self._verbose:
153
- for entry in batch.entries:
154
- if entry.errors:
155
- self._tqdm.write("Failed entry: {}".format(entry))
156
-
157
- def reader_error(self, reader, exception):
158
- super().reader_error(reader, exception)
159
- self._tqdm.write("Reader error:{}: {}".format(reader, exception))
160
-
161
- def transformer_error(self, batch, transformer, exception):
162
- super().transformer_error(batch, transformer, exception)
163
- self._tqdm.write("Transformer error: {}: {}".format(transformer, exception))
164
-
165
- def writer_error(self, batch, writer, exception):
166
- super().writer_error(batch, writer, exception)
167
- self._tqdm.write("Writer error: {}: {}".format(writer, exception))