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
oarepo_runtime/info/views.py
DELETED
@@ -1,586 +0,0 @@
|
|
1
|
-
import importlib
|
2
|
-
import json
|
3
|
-
import logging
|
4
|
-
import os
|
5
|
-
import re
|
6
|
-
from functools import cached_property
|
7
|
-
from urllib.parse import urljoin, urlparse, urlunparse
|
8
|
-
|
9
|
-
import importlib_metadata
|
10
|
-
import importlib_resources
|
11
|
-
import marshmallow as ma
|
12
|
-
from flask import current_app, request, url_for
|
13
|
-
from flask.ctx import RequestContext
|
14
|
-
from flask.globals import _cv_request
|
15
|
-
from flask_resources import (
|
16
|
-
Resource,
|
17
|
-
ResourceConfig,
|
18
|
-
from_conf,
|
19
|
-
request_parser,
|
20
|
-
resource_requestctx,
|
21
|
-
response_handler,
|
22
|
-
route,
|
23
|
-
)
|
24
|
-
from flask_restful import abort
|
25
|
-
from invenio_base.utils import obj_or_import_string
|
26
|
-
from invenio_jsonschemas import current_jsonschemas
|
27
|
-
from invenio_records_resources.proxies import (
|
28
|
-
current_service_registry,
|
29
|
-
current_transfer_registry,
|
30
|
-
)
|
31
|
-
from invenio_records_resources.records.api import Record
|
32
|
-
|
33
|
-
logger = logging.getLogger("oarepo_runtime.info")
|
34
|
-
|
35
|
-
|
36
|
-
class InfoConfig(ResourceConfig):
|
37
|
-
blueprint_name = "oarepo_runtime_info"
|
38
|
-
url_prefix = "/.well-known/repository"
|
39
|
-
|
40
|
-
schema_view_args = {"schema": ma.fields.Str()}
|
41
|
-
model_view_args = {"model": ma.fields.Str()}
|
42
|
-
|
43
|
-
def __init__(self, app):
|
44
|
-
self.app = app
|
45
|
-
|
46
|
-
@cached_property
|
47
|
-
def components(self):
|
48
|
-
return tuple(
|
49
|
-
obj_or_import_string(x)
|
50
|
-
for x in self.app.config.get("INFO_ENDPOINT_COMPONENTS", [])
|
51
|
-
)
|
52
|
-
|
53
|
-
|
54
|
-
schema_view_args = request_parser(from_conf("schema_view_args"), location="view_args")
|
55
|
-
model_view_args = request_parser(from_conf("model_view_args"), location="view_args")
|
56
|
-
|
57
|
-
|
58
|
-
class InfoResource(Resource):
|
59
|
-
def create_url_rules(self):
|
60
|
-
return [
|
61
|
-
route("GET", "/", self.repository),
|
62
|
-
route("GET", "/models", self.models),
|
63
|
-
route("GET", "/schema/<path:schema>", self.schema),
|
64
|
-
route("GET", "/models/<model>", self.model),
|
65
|
-
]
|
66
|
-
|
67
|
-
@cached_property
|
68
|
-
def components(self):
|
69
|
-
return [x(self) for x in self.config.components]
|
70
|
-
|
71
|
-
@response_handler()
|
72
|
-
def repository(self):
|
73
|
-
"""Repository endpoint."""
|
74
|
-
links = {
|
75
|
-
"self": url_for(request.endpoint, _external=True),
|
76
|
-
"api": replace_path_in_url(
|
77
|
-
url_for(request.endpoint, _external=True), "/api"
|
78
|
-
),
|
79
|
-
"models": url_for("oarepo_runtime_info.models", _external=True),
|
80
|
-
}
|
81
|
-
try:
|
82
|
-
import invenio_requests # noqa
|
83
|
-
|
84
|
-
links["requests"] = api_url_for("requests.search", _external=True)
|
85
|
-
except ImportError:
|
86
|
-
pass
|
87
|
-
|
88
|
-
ret = {
|
89
|
-
"schema": "local://introspection-v1.0.0",
|
90
|
-
"name": current_app.config.get("THEME_SITENAME", ""),
|
91
|
-
"description": current_app.config.get("REPOSITORY_DESCRIPTION", ""),
|
92
|
-
"version": os.environ.get("DEPLOYMENT_VERSION", "local development"),
|
93
|
-
"invenio_version": get_package_version("oarepo"),
|
94
|
-
"transfers": list(current_transfer_registry.get_transfer_types()),
|
95
|
-
"links": links,
|
96
|
-
"features": [
|
97
|
-
*_add_feature_if_can_import("drafts", "invenio_drafts_resources"),
|
98
|
-
*_add_feature_if_can_import("workflows", "oarepo_workflows"),
|
99
|
-
*_add_feature_if_can_import("requests", "invenio_requests"),
|
100
|
-
*_add_feature_if_can_import("communities", "invenio_communities"),
|
101
|
-
*_add_feature_if_can_import("request_types", "oarepo_requests"),
|
102
|
-
],
|
103
|
-
}
|
104
|
-
if len(self.model_data) == 1:
|
105
|
-
ret["default_model"] = self.model_data[0]["name"]
|
106
|
-
|
107
|
-
self.call_components("repository", data=ret)
|
108
|
-
return ret, 200
|
109
|
-
|
110
|
-
@cached_property
|
111
|
-
def model_data(self):
|
112
|
-
data = []
|
113
|
-
# iterate entrypoint oarepo.models
|
114
|
-
for model in importlib_metadata.entry_points().select(group="oarepo.models"):
|
115
|
-
package_name, file_name = model.value.split(":")
|
116
|
-
model_data = json.loads(
|
117
|
-
importlib_resources.files(package_name).joinpath(file_name).read_text()
|
118
|
-
)
|
119
|
-
model_data = model_data.get("model", {})
|
120
|
-
if model_data.get("type") != "model":
|
121
|
-
continue
|
122
|
-
|
123
|
-
resource_config_class = self._get_resource_config_class(model_data)
|
124
|
-
service = self._get_service(model_data)
|
125
|
-
service_class = self._get_service_class(model_data)
|
126
|
-
if not service or type(service) != service_class:
|
127
|
-
continue
|
128
|
-
|
129
|
-
# check if the service class is inside OAREPO_GLOBAL_SEARCH and if not, skip it
|
130
|
-
global_search_models = current_app.config.get("GLOBAL_SEARCH_MODELS", [])
|
131
|
-
for global_model in global_search_models:
|
132
|
-
if global_model["model_service"] == model_data["service"]["class"]:
|
133
|
-
break
|
134
|
-
else:
|
135
|
-
continue
|
136
|
-
|
137
|
-
model_features = self._get_model_features(model_data)
|
138
|
-
|
139
|
-
links = {
|
140
|
-
"html": self._get_model_html_endpoint(model_data),
|
141
|
-
"model": self._get_model_model_endpoint(model.name),
|
142
|
-
# "openapi": url_for(self._get_model_openapi_endpoint(model_data), _external=True)
|
143
|
-
}
|
144
|
-
|
145
|
-
links["records"] = self._get_model_api_endpoint(model_data)
|
146
|
-
if "drafts" in model_features:
|
147
|
-
links["drafts"] = self._get_model_draft_endpoint(model_data)
|
148
|
-
links["deposit"] = links["records"]
|
149
|
-
|
150
|
-
data.append(
|
151
|
-
{
|
152
|
-
"schema": "local://" + model_data["json-schema-settings"]["name"],
|
153
|
-
"type": model_data.get(
|
154
|
-
"model-name", model_data.get("module", {}).get("base", "")
|
155
|
-
).lower(),
|
156
|
-
"name": model_data.get(
|
157
|
-
"model-name", model_data.get("module", {}).get("base", "")
|
158
|
-
).lower(),
|
159
|
-
"description": model_data.get("model-description", ""),
|
160
|
-
"version": model_data["json-schema-settings"]["version"],
|
161
|
-
"features": model_features,
|
162
|
-
"links": links,
|
163
|
-
# TODO: we also need to get previous schema versions here if we support
|
164
|
-
# multiple version of the same schema at the same time
|
165
|
-
"content_types": self._get_model_content_types(
|
166
|
-
service, resource_config_class, model_data
|
167
|
-
),
|
168
|
-
"metadata": model_data.get("properties", {}).get("metadata", None)
|
169
|
-
is not None,
|
170
|
-
}
|
171
|
-
)
|
172
|
-
self.call_components("model", data=data)
|
173
|
-
data.sort(key=lambda x: x["type"])
|
174
|
-
return data
|
175
|
-
|
176
|
-
@cached_property
|
177
|
-
def vocabulary_data(self):
|
178
|
-
ret = []
|
179
|
-
try:
|
180
|
-
from invenio_vocabularies.contrib.affiliations.api import Affiliation
|
181
|
-
from invenio_vocabularies.contrib.awards.api import Award
|
182
|
-
from invenio_vocabularies.contrib.funders.api import Funder
|
183
|
-
from invenio_vocabularies.contrib.names.api import Name
|
184
|
-
from invenio_vocabularies.contrib.subjects.api import Subject
|
185
|
-
from invenio_vocabularies.records.api import Vocabulary
|
186
|
-
from invenio_vocabularies.records.models import VocabularyType
|
187
|
-
except ImportError:
|
188
|
-
return ret
|
189
|
-
|
190
|
-
def _generate_rdm_vocabulary(
|
191
|
-
base_url: str,
|
192
|
-
record: type[Record],
|
193
|
-
vocabulary_type: str,
|
194
|
-
vocabulary_name: str,
|
195
|
-
vocabulary_description: str,
|
196
|
-
special: bool,
|
197
|
-
can_export: bool = True,
|
198
|
-
can_deposit: bool = False,
|
199
|
-
) -> dict:
|
200
|
-
if not base_url.endswith("/"):
|
201
|
-
base_url += "/"
|
202
|
-
url_prefix = base_url + "api" if special else base_url + "api/vocabularies"
|
203
|
-
schema_path = base_url + record.schema.value.replace("local://", "schemas/")
|
204
|
-
links = dict(
|
205
|
-
records=f"{url_prefix}/{vocabulary_type}",
|
206
|
-
)
|
207
|
-
if can_deposit:
|
208
|
-
links["deposit"] = f"{url_prefix}/{vocabulary_type}"
|
209
|
-
|
210
|
-
return dict(
|
211
|
-
schema=record.schema.value,
|
212
|
-
type=vocabulary_type,
|
213
|
-
name=vocabulary_name,
|
214
|
-
description="Vocabulary for " + vocabulary_name,
|
215
|
-
version="unknown",
|
216
|
-
features=["rdm", "vocabulary"],
|
217
|
-
links=links,
|
218
|
-
content_types=[
|
219
|
-
dict(
|
220
|
-
content_type="application/json",
|
221
|
-
name="Invenio RDM JSON",
|
222
|
-
description="Vocabulary JSON",
|
223
|
-
schema=schema_path,
|
224
|
-
can_export=can_export,
|
225
|
-
can_deposit=can_deposit,
|
226
|
-
)
|
227
|
-
],
|
228
|
-
metadata=False,
|
229
|
-
)
|
230
|
-
|
231
|
-
base_url = api_url_for("vocabularies.search", type="languages", _external=True)
|
232
|
-
base_url = replace_path_in_url(base_url, "/")
|
233
|
-
ret = [
|
234
|
-
_generate_rdm_vocabulary(
|
235
|
-
base_url, Affiliation, "affiliations", "Affiliations", "", special=True
|
236
|
-
),
|
237
|
-
_generate_rdm_vocabulary(
|
238
|
-
base_url, Award, "awards", "Awards", "", special=True
|
239
|
-
),
|
240
|
-
_generate_rdm_vocabulary(
|
241
|
-
base_url, Funder, "funders", "Funders", "", special=True
|
242
|
-
),
|
243
|
-
_generate_rdm_vocabulary(
|
244
|
-
base_url, Subject, "subjects", "Subjects", "", special=True
|
245
|
-
),
|
246
|
-
_generate_rdm_vocabulary(
|
247
|
-
base_url, Name, "names", "Names", "", special=True
|
248
|
-
),
|
249
|
-
_generate_rdm_vocabulary(
|
250
|
-
base_url,
|
251
|
-
Affiliation,
|
252
|
-
"affiliations-vocab",
|
253
|
-
"Writable Affiliations",
|
254
|
-
"Write endpoint for affiliations",
|
255
|
-
special=False,
|
256
|
-
can_deposit=True,
|
257
|
-
),
|
258
|
-
_generate_rdm_vocabulary(
|
259
|
-
base_url,
|
260
|
-
Award,
|
261
|
-
"awards-vocab",
|
262
|
-
"Writable Awards",
|
263
|
-
"Write endpoint for awards",
|
264
|
-
special=False,
|
265
|
-
can_deposit=True,
|
266
|
-
),
|
267
|
-
_generate_rdm_vocabulary(
|
268
|
-
base_url,
|
269
|
-
Funder,
|
270
|
-
"funders-vocab",
|
271
|
-
"Writable Funders",
|
272
|
-
"Write endpoint for funders",
|
273
|
-
special=False,
|
274
|
-
can_deposit=True,
|
275
|
-
),
|
276
|
-
_generate_rdm_vocabulary(
|
277
|
-
base_url,
|
278
|
-
Subject,
|
279
|
-
"subjects-vocab",
|
280
|
-
"Writable Subjects",
|
281
|
-
"Write endpoint for subjects",
|
282
|
-
special=False,
|
283
|
-
can_deposit=True,
|
284
|
-
),
|
285
|
-
_generate_rdm_vocabulary(
|
286
|
-
base_url,
|
287
|
-
Name,
|
288
|
-
"names-vocab",
|
289
|
-
"Writable Names",
|
290
|
-
"Write endpoint for names",
|
291
|
-
special=False,
|
292
|
-
can_deposit=True,
|
293
|
-
),
|
294
|
-
]
|
295
|
-
|
296
|
-
vc_types = {vc.id for vc in VocabularyType.query.all()}
|
297
|
-
vocab_type_metadata = current_app.config.get(
|
298
|
-
"INVENIO_VOCABULARY_TYPE_METADATA", {}
|
299
|
-
)
|
300
|
-
vc_types.update(vocab_type_metadata.keys())
|
301
|
-
|
302
|
-
for vc in sorted(vc_types):
|
303
|
-
vc_metadata = vocab_type_metadata.get(vc, {})
|
304
|
-
ret.append(
|
305
|
-
_generate_rdm_vocabulary(
|
306
|
-
base_url,
|
307
|
-
Vocabulary,
|
308
|
-
vc,
|
309
|
-
to_current_language(vc_metadata.get("name")) or vc,
|
310
|
-
to_current_language(vc_metadata.get("description")) or "",
|
311
|
-
special=False,
|
312
|
-
can_export=True,
|
313
|
-
can_deposit=True,
|
314
|
-
)
|
315
|
-
)
|
316
|
-
|
317
|
-
return ret
|
318
|
-
|
319
|
-
@response_handler(many=True)
|
320
|
-
def models(self):
|
321
|
-
return self.model_data + self.vocabulary_data, 200
|
322
|
-
|
323
|
-
@schema_view_args
|
324
|
-
@response_handler()
|
325
|
-
def schema(self):
|
326
|
-
schema = resource_requestctx.view_args["schema"]
|
327
|
-
return current_jsonschemas.get_schema(schema, resolved=True), 200
|
328
|
-
|
329
|
-
@model_view_args
|
330
|
-
@response_handler()
|
331
|
-
def model(self):
|
332
|
-
model = resource_requestctx.view_args["model"]
|
333
|
-
for _model in importlib_metadata.entry_points().select(
|
334
|
-
group="oarepo.models", name=model
|
335
|
-
):
|
336
|
-
package_name, file_name = _model.value.split(":")
|
337
|
-
model_data = json.loads(
|
338
|
-
importlib_resources.files(package_name).joinpath(file_name).read_text()
|
339
|
-
)
|
340
|
-
return self._remove_implementation_details_from_model(model_data), 200
|
341
|
-
abort(404)
|
342
|
-
|
343
|
-
IMPLEMENTATION_DETAILS = re.compile(
|
344
|
-
r"""
|
345
|
-
^(
|
346
|
-
class |
|
347
|
-
.*-class |
|
348
|
-
base-classes |
|
349
|
-
.*-base-classes |
|
350
|
-
module |
|
351
|
-
generate |
|
352
|
-
imports |
|
353
|
-
extra-code |
|
354
|
-
components |
|
355
|
-
.*-args
|
356
|
-
)$
|
357
|
-
""",
|
358
|
-
re.VERBOSE,
|
359
|
-
)
|
360
|
-
|
361
|
-
def _remove_implementation_details_from_model(self, model):
|
362
|
-
if isinstance(model, dict):
|
363
|
-
return self._remove_implementation_details_from_model_dict(model)
|
364
|
-
elif isinstance(model, list):
|
365
|
-
return self._remove_implementation_details_from_model_list(model)
|
366
|
-
else:
|
367
|
-
return model
|
368
|
-
|
369
|
-
def _remove_implementation_details_from_model_dict(self, model):
|
370
|
-
ret = {}
|
371
|
-
for k, v in model.items():
|
372
|
-
if not self.IMPLEMENTATION_DETAILS.match(k):
|
373
|
-
new_value = self._remove_implementation_details_from_model(v)
|
374
|
-
if new_value is not None and new_value != {} and new_value != []:
|
375
|
-
ret[k] = new_value
|
376
|
-
return ret
|
377
|
-
|
378
|
-
def _remove_implementation_details_from_model_list(self, model):
|
379
|
-
ret = []
|
380
|
-
for v in model:
|
381
|
-
new_value = self._remove_implementation_details_from_model(v)
|
382
|
-
if new_value is not None and new_value != {} and new_value != []:
|
383
|
-
ret.append(new_value)
|
384
|
-
return ret
|
385
|
-
|
386
|
-
def call_components(self, method_name, **kwargs):
|
387
|
-
for component in self.components:
|
388
|
-
if hasattr(component, method_name):
|
389
|
-
getattr(component, method_name)(**kwargs)
|
390
|
-
|
391
|
-
def _get_model_features(self, model):
|
392
|
-
features = []
|
393
|
-
if model.get("requests", {}):
|
394
|
-
features.append("requests")
|
395
|
-
if model.get("draft", {}):
|
396
|
-
features.append("drafts")
|
397
|
-
if model.get("files", {}):
|
398
|
-
features.append("files")
|
399
|
-
return features
|
400
|
-
|
401
|
-
def _get_model_api_endpoint(self, model):
|
402
|
-
try:
|
403
|
-
alias = model["api-blueprint"]["alias"]
|
404
|
-
return api_url_for(f"{alias}.search", _external=True)
|
405
|
-
except: # NOSONAR noqa
|
406
|
-
logger.exception("Failed to get model api endpoint")
|
407
|
-
return None
|
408
|
-
|
409
|
-
def _get_model_draft_endpoint(self, model):
|
410
|
-
try:
|
411
|
-
alias = model["api-blueprint"]["alias"]
|
412
|
-
return api_url_for(f"{alias}.search_user_records", _external=True)
|
413
|
-
except: # NOSONAR noqa
|
414
|
-
logger.exception("Failed to get model draft endpoint")
|
415
|
-
return None
|
416
|
-
|
417
|
-
def _get_model_html_endpoint(self, model):
|
418
|
-
try:
|
419
|
-
return urljoin(
|
420
|
-
self._get_model_api_endpoint(model),
|
421
|
-
model["resource-config"]["base-html-url"],
|
422
|
-
)
|
423
|
-
except: # NOSONAR noqa
|
424
|
-
logger.exception("Failed to get model html endpoint")
|
425
|
-
return None
|
426
|
-
|
427
|
-
def _get_model_model_endpoint(self, model):
|
428
|
-
try:
|
429
|
-
return url_for("oarepo_runtime_info.model", model=model, _external=True)
|
430
|
-
except: # NOSONAR noqa
|
431
|
-
logger.exception("Failed to get model model endpoint")
|
432
|
-
return None
|
433
|
-
|
434
|
-
def _get_model_schema_endpoints(self, model):
|
435
|
-
try:
|
436
|
-
return {
|
437
|
-
"application/json": url_for(
|
438
|
-
"oarepo_runtime_info.schema",
|
439
|
-
schema=model["json-schema-settings"]["name"],
|
440
|
-
_external=True,
|
441
|
-
)
|
442
|
-
}
|
443
|
-
except: # NOSONAR noqa
|
444
|
-
logger.exception("Failed to get model schema endpoint")
|
445
|
-
return None
|
446
|
-
|
447
|
-
def _get_model_content_types(self, service, resource_config, model):
|
448
|
-
"""Get the content types supported by the model. Returns a list of:
|
449
|
-
|
450
|
-
content_type="application/json",
|
451
|
-
name="Invenio RDM JSON",
|
452
|
-
description="Invenio RDM JSON as described in",
|
453
|
-
schema=url / "schemas" / "records" / "record-v6.0.0.json",
|
454
|
-
can_export=True,
|
455
|
-
can_deposit=True,
|
456
|
-
"""
|
457
|
-
|
458
|
-
content_types = []
|
459
|
-
# implicit content type
|
460
|
-
content_types.append(
|
461
|
-
{
|
462
|
-
"content_type": "application/json",
|
463
|
-
"name": f"Internal json serialization of {model['model-name']}",
|
464
|
-
"description": "This content type is serving this model's native format as described on model link.",
|
465
|
-
"schema": url_for(
|
466
|
-
"oarepo_runtime_info.schema",
|
467
|
-
schema=model["json-schema-settings"]["name"],
|
468
|
-
_external=True,
|
469
|
-
),
|
470
|
-
"can_export": True,
|
471
|
-
"can_deposit": True,
|
472
|
-
}
|
473
|
-
)
|
474
|
-
|
475
|
-
# export content types
|
476
|
-
try:
|
477
|
-
for accept_type, handler in resource_config.response_handlers.items():
|
478
|
-
if accept_type == "application/json":
|
479
|
-
continue
|
480
|
-
curr_item = {
|
481
|
-
"content_type": accept_type,
|
482
|
-
"name": getattr(handler, "name", accept_type),
|
483
|
-
"description": getattr(handler, "description", ""),
|
484
|
-
"can_export": True,
|
485
|
-
"can_deposit": False,
|
486
|
-
}
|
487
|
-
if handler.serializer is not None:
|
488
|
-
if hasattr(handler.serializer, "name"):
|
489
|
-
curr_item["name"] = handler.serializer.name
|
490
|
-
if hasattr(handler.serializer, "description"):
|
491
|
-
curr_item["description"] = handler.serializer.description
|
492
|
-
if hasattr(handler.serializer, "info"):
|
493
|
-
curr_item.update(handler.serializer.info(service))
|
494
|
-
content_types.append(curr_item)
|
495
|
-
except: # NOSONAR noqa
|
496
|
-
logger.exception("Failed to get model schemas")
|
497
|
-
return content_types
|
498
|
-
|
499
|
-
def _get_resource_config_class(self, model_data):
|
500
|
-
model_class = model_data["resource-config"]["class"]
|
501
|
-
return obj_or_import_string(model_class)()
|
502
|
-
|
503
|
-
def _get_service(self, model_data):
|
504
|
-
service_id = model_data["service-config"]["service-id"]
|
505
|
-
try:
|
506
|
-
service = current_service_registry.get(service_id)
|
507
|
-
except KeyError:
|
508
|
-
return None
|
509
|
-
return service
|
510
|
-
|
511
|
-
def _get_service_class(self, model_data):
|
512
|
-
service_id = model_data["service"]["class"]
|
513
|
-
return obj_or_import_string(service_id)
|
514
|
-
|
515
|
-
|
516
|
-
def create_wellknown_blueprint(app):
|
517
|
-
"""Create blueprint."""
|
518
|
-
config_class = obj_or_import_string(
|
519
|
-
app.config.get("INFO_ENDPOINT_CONFIG", InfoConfig)
|
520
|
-
)
|
521
|
-
return InfoResource(config=config_class(app)).as_blueprint()
|
522
|
-
|
523
|
-
|
524
|
-
def get_package_version(package_name):
|
525
|
-
"""Get package version."""
|
526
|
-
from pkg_resources import get_distribution
|
527
|
-
|
528
|
-
try:
|
529
|
-
return re.sub(r"\+.*", "", get_distribution(package_name).version)
|
530
|
-
except Exception: # NOSONAR noqa
|
531
|
-
logger.exception(f"Failed to get package version for {package_name}")
|
532
|
-
return None
|
533
|
-
|
534
|
-
|
535
|
-
def api_url_for(endpoint, _external=True, **values):
|
536
|
-
"""API url_for."""
|
537
|
-
try:
|
538
|
-
api_app = current_app.wsgi_app.mounts["/api"]
|
539
|
-
except:
|
540
|
-
api_app = current_app
|
541
|
-
|
542
|
-
site_api_url = current_app.config["SITE_API_URL"]
|
543
|
-
site_url = current_app.config["SITE_UI_URL"]
|
544
|
-
current_request_context = _cv_request.get()
|
545
|
-
try:
|
546
|
-
new_context = RequestContext(app=api_app, environ=request.environ)
|
547
|
-
_cv_request.set(new_context)
|
548
|
-
base_url = api_app.url_for(endpoint, **values, _external=_external)
|
549
|
-
if base_url.startswith(site_api_url):
|
550
|
-
return base_url
|
551
|
-
if base_url.startswith(site_url):
|
552
|
-
return base_url.replace(site_url, site_api_url)
|
553
|
-
raise ValueError(
|
554
|
-
f"URL {base_url} does not start with {site_url} or {site_api_url}"
|
555
|
-
)
|
556
|
-
finally:
|
557
|
-
_cv_request.set(current_request_context)
|
558
|
-
|
559
|
-
|
560
|
-
def replace_path_in_url(url, path):
|
561
|
-
# Parse the URL into its components
|
562
|
-
parsed_url = urlparse(url)
|
563
|
-
|
564
|
-
# Replace the path with '/api'
|
565
|
-
new_parsed_url = parsed_url._replace(path=path)
|
566
|
-
|
567
|
-
# Reconstruct the URL with the new path
|
568
|
-
new_url = urlunparse(new_parsed_url)
|
569
|
-
|
570
|
-
return new_url
|
571
|
-
|
572
|
-
|
573
|
-
def _add_feature_if_can_import(feature, module):
|
574
|
-
try:
|
575
|
-
importlib.import_module(module)
|
576
|
-
return [feature]
|
577
|
-
except ImportError:
|
578
|
-
return []
|
579
|
-
|
580
|
-
|
581
|
-
def to_current_language(data):
|
582
|
-
if isinstance(data, dict):
|
583
|
-
from flask_babel import get_locale
|
584
|
-
|
585
|
-
return data.get(get_locale().language)
|
586
|
-
return data
|
oarepo_runtime/profile.py
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
# profiling support
|
2
|
-
import atexit
|
3
|
-
import contextlib
|
4
|
-
import functools
|
5
|
-
import math
|
6
|
-
import time
|
7
|
-
from collections import Counter
|
8
|
-
|
9
|
-
|
10
|
-
class ProfilingTimer:
|
11
|
-
def __init__(self):
|
12
|
-
self.counts = Counter()
|
13
|
-
self.sums = Counter()
|
14
|
-
self.sums_squared = Counter()
|
15
|
-
|
16
|
-
def time(self, function):
|
17
|
-
@functools.wraps(function)
|
18
|
-
def inner(*args, **kwargs):
|
19
|
-
start = time.time()
|
20
|
-
try:
|
21
|
-
return function(*args, **kwargs)
|
22
|
-
finally:
|
23
|
-
stop = time.time()
|
24
|
-
self.register(function, start, stop)
|
25
|
-
|
26
|
-
return inner
|
27
|
-
|
28
|
-
def register(self, function, start, stop):
|
29
|
-
self.counts[function] += 1
|
30
|
-
duration = stop - start
|
31
|
-
self.sums[function] += duration
|
32
|
-
self.sums_squared[function] += duration * duration
|
33
|
-
|
34
|
-
@contextlib.contextmanager
|
35
|
-
def time_block(self, name):
|
36
|
-
start = time.time()
|
37
|
-
try:
|
38
|
-
yield
|
39
|
-
finally:
|
40
|
-
stop = time.time()
|
41
|
-
self.register(name, start, stop)
|
42
|
-
|
43
|
-
def display(self):
|
44
|
-
for f in self.counts:
|
45
|
-
print(f)
|
46
|
-
print(f"Call count : {self.counts[f]}")
|
47
|
-
print(f"Total time : {self.sums[f]}")
|
48
|
-
print(f"Avg time : {self.sums[f] / self.counts[f]}")
|
49
|
-
std_deviation = (
|
50
|
-
math.sqrt(
|
51
|
-
self.counts[f] * self.sums_squared[f] - self.sums[f] * self.sums[f]
|
52
|
-
)
|
53
|
-
/ self.counts[f]
|
54
|
-
)
|
55
|
-
print(f"Std deviation : {std_deviation}")
|
56
|
-
print()
|
57
|
-
|
58
|
-
|
59
|
-
timer = ProfilingTimer()
|
60
|
-
atexit.register(timer.display)
|
@@ -1,38 +0,0 @@
|
|
1
|
-
from invenio_records.dumpers import SearchDumperExt
|
2
|
-
from sqlalchemy.util import classproperty
|
3
|
-
|
4
|
-
from oarepo_runtime.utils.path import PathTraversal
|
5
|
-
|
6
|
-
|
7
|
-
class EDTFIntervalDumperExt(SearchDumperExt):
|
8
|
-
paths = []
|
9
|
-
_path_traversal = None
|
10
|
-
|
11
|
-
@classproperty
|
12
|
-
def path_traversal(cls):
|
13
|
-
if cls._path_traversal is None:
|
14
|
-
cls._path_traversal = PathTraversal(cls.paths)
|
15
|
-
return cls._path_traversal
|
16
|
-
|
17
|
-
def dump(self, record, data):
|
18
|
-
for path in self.path_traversal.iter(data):
|
19
|
-
rec = path[-1].current
|
20
|
-
rec = rec.split("/")
|
21
|
-
search_range = {}
|
22
|
-
if rec[0]:
|
23
|
-
search_range["gte"] = rec[0].strip()
|
24
|
-
if len(rec) > 1 and rec[1]:
|
25
|
-
search_range["lte"] = rec[1].strip()
|
26
|
-
path[-1].parent_data[path[-1].key] = search_range
|
27
|
-
return data
|
28
|
-
|
29
|
-
def load(self, data, record_cls):
|
30
|
-
for path in self.path_traversal.iter(data):
|
31
|
-
rec = path[-1].current
|
32
|
-
if rec:
|
33
|
-
path[-1].parent_data[
|
34
|
-
path[-1].key
|
35
|
-
] = f"{rec.get('gte', '')}/{rec.get('lte', '')}"
|
36
|
-
else:
|
37
|
-
del path[-1].parent_data[path[-1].key]
|
38
|
-
return data
|