oarepo-runtime 2.0.0.dev10__py3-none-any.whl → 2.0.0.dev12__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 +1 -1
- oarepo_runtime/api.py +39 -2
- oarepo_runtime/cli/__init__.py +1 -1
- oarepo_runtime/cli/search.py +4 -3
- oarepo_runtime/ext.py +116 -2
- oarepo_runtime/records/pid_providers.py +2 -1
- {oarepo_runtime-2.0.0.dev10.dist-info → oarepo_runtime-2.0.0.dev12.dist-info}/METADATA +1 -1
- {oarepo_runtime-2.0.0.dev10.dist-info → oarepo_runtime-2.0.0.dev12.dist-info}/RECORD +11 -11
- {oarepo_runtime-2.0.0.dev10.dist-info → oarepo_runtime-2.0.0.dev12.dist-info}/entry_points.txt +3 -0
- {oarepo_runtime-2.0.0.dev10.dist-info → oarepo_runtime-2.0.0.dev12.dist-info}/WHEEL +0 -0
- {oarepo_runtime-2.0.0.dev10.dist-info → oarepo_runtime-2.0.0.dev12.dist-info}/licenses/LICENSE +0 -0
oarepo_runtime/__init__.py
CHANGED
oarepo_runtime/api.py
CHANGED
@@ -27,7 +27,11 @@ if TYPE_CHECKING:
|
|
27
27
|
from flask_resources.responses import ResponseHandler
|
28
28
|
from flask_resources.serializers import BaseSerializer
|
29
29
|
from invenio_drafts_resources.records.api import Draft
|
30
|
-
from invenio_records_resources.records.api import
|
30
|
+
from invenio_records_resources.records.api import Record
|
31
|
+
from invenio_records_resources.records.systemfields.pid import (
|
32
|
+
ModelPIDField,
|
33
|
+
ModelPIDFieldContext,
|
34
|
+
)
|
31
35
|
from invenio_records_resources.resources.records.config import RecordResourceConfig
|
32
36
|
from invenio_records_resources.resources.records.resource import RecordResource
|
33
37
|
from invenio_records_resources.services import (
|
@@ -72,7 +76,7 @@ class Export:
|
|
72
76
|
class Model[
|
73
77
|
S: RecordService = RecordService,
|
74
78
|
C: RecordServiceConfig = RecordServiceConfig,
|
75
|
-
R:
|
79
|
+
R: Record = Record,
|
76
80
|
D: Draft = Draft,
|
77
81
|
# not sure why this is flagged by pyright as an error
|
78
82
|
RR: RecordResource = RecordResource, # pyright: ignore[reportGeneralTypeIssues]
|
@@ -242,6 +246,39 @@ class Model[
|
|
242
246
|
"""Get the API blueprint name for the model."""
|
243
247
|
return cast("str", self.resource_config.blueprint_name)
|
244
248
|
|
249
|
+
@property
|
250
|
+
def record_pid_type(self) -> str | None:
|
251
|
+
"""Get the PID type for the model."""
|
252
|
+
return self._pid_type_from_record(self.record_cls)
|
253
|
+
|
254
|
+
@property
|
255
|
+
def record_json_schema(self) -> str:
|
256
|
+
"""Get the json schema of the record."""
|
257
|
+
return self.record_cls.schema.value # type: ignore[attr-defined, no-any-return]
|
258
|
+
|
259
|
+
@property
|
260
|
+
def draft_pid_type(self) -> str | None:
|
261
|
+
"""Get the PID type for the model."""
|
262
|
+
return self._pid_type_from_record(self.draft_cls)
|
263
|
+
|
264
|
+
def _pid_type_from_record(self, record_cls: type[Record] | None) -> str | None:
|
265
|
+
"""Get the PID type from a record class, returning None if not found."""
|
266
|
+
if record_cls is None:
|
267
|
+
return None
|
268
|
+
pid_context: ModelPIDFieldContext | None = getattr(record_cls, "pid", None)
|
269
|
+
if pid_context is None:
|
270
|
+
# registered record has no pid field
|
271
|
+
return None # pragma: no cover
|
272
|
+
pid_field: ModelPIDField | None = getattr(pid_context, "field", None)
|
273
|
+
if pid_field is None:
|
274
|
+
# there is no pid field in the context
|
275
|
+
return None # pragma: no cover
|
276
|
+
pid_provider = getattr(pid_field, "_provider", None)
|
277
|
+
if not pid_provider:
|
278
|
+
# there is no pid provider in the field
|
279
|
+
return None # pragma: no cover
|
280
|
+
return getattr(pid_provider, "pid_type", None)
|
281
|
+
|
245
282
|
def api_url(self, view_name: str, **kwargs: Any) -> str:
|
246
283
|
"""Get the API URL for the model."""
|
247
284
|
return cast("str", invenio_url_for(f"{self.api_blueprint_name}.{view_name}", **kwargs))
|
oarepo_runtime/cli/__init__.py
CHANGED
oarepo_runtime/cli/search.py
CHANGED
@@ -10,13 +10,13 @@
|
|
10
10
|
|
11
11
|
from __future__ import annotations
|
12
12
|
|
13
|
+
from importlib import metadata as importlib_metadata
|
14
|
+
|
13
15
|
import click
|
14
16
|
from flask.cli import with_appcontext
|
15
17
|
from invenio_search.cli import index, search_version_check
|
16
18
|
from invenio_search.cli import init as original_init
|
17
19
|
|
18
|
-
from oarepo_runtime.services.records.mapping import update_all_records_mappings
|
19
|
-
|
20
20
|
|
21
21
|
@index.command()
|
22
22
|
@click.option("--force", is_flag=True, default=False)
|
@@ -31,4 +31,5 @@ def init(ctx: click.Context, force: bool) -> None:
|
|
31
31
|
defined inside the models.
|
32
32
|
"""
|
33
33
|
ctx.invoke(original_init, force=force)
|
34
|
-
|
34
|
+
for ep in importlib_metadata.entry_points(group="oarepo.cli.search.init"):
|
35
|
+
ep.load()()
|
oarepo_runtime/ext.py
CHANGED
@@ -15,13 +15,19 @@ from functools import cached_property
|
|
15
15
|
from typing import TYPE_CHECKING, Any, cast
|
16
16
|
|
17
17
|
from flask import current_app
|
18
|
+
from invenio_pidstore.errors import PIDDoesNotExistError
|
19
|
+
from invenio_pidstore.models import PersistentIdentifier
|
18
20
|
from invenio_records_resources.proxies import current_service_registry
|
19
|
-
from invenio_records_resources.records.api import RecordBase
|
21
|
+
from invenio_records_resources.records.api import Record, RecordBase
|
20
22
|
|
21
23
|
from . import config
|
22
24
|
|
23
25
|
if TYPE_CHECKING: # pragma: no cover
|
26
|
+
from collections.abc import Iterable
|
27
|
+
from uuid import UUID
|
28
|
+
|
24
29
|
from flask import Flask
|
30
|
+
from invenio_drafts_resources.records.api import Draft
|
25
31
|
from invenio_records_resources.services.base.service import Service
|
26
32
|
from invenio_records_resources.services.records import RecordService
|
27
33
|
|
@@ -54,13 +60,104 @@ class OARepoRuntime:
|
|
54
60
|
"""Return the models registered in the extension."""
|
55
61
|
return cast("dict[str, Model]", current_app.config["OAREPO_MODELS"])
|
56
62
|
|
63
|
+
@property
|
64
|
+
def rdm_models(self) -> Iterable[Model]:
|
65
|
+
"""Return the RDM models registered in the extension."""
|
66
|
+
return [v for v in self.models.values() if v.records_alias_enabled]
|
67
|
+
|
57
68
|
@cached_property
|
58
|
-
def models_by_record_class(self) -> dict[type[
|
69
|
+
def models_by_record_class(self) -> dict[type[Record], Model]:
|
59
70
|
"""Return a mapping of record classes to their models."""
|
60
71
|
ret = {model.record_cls: model for model in self.models.values() if model.record_cls is not None}
|
61
72
|
ret.update({model.draft_cls: model for model in self.models.values() if model.draft_cls is not None})
|
62
73
|
return ret
|
63
74
|
|
75
|
+
@cached_property
|
76
|
+
def record_class_by_pid_type(self) -> dict[str, type[Record]]:
|
77
|
+
"""Return a mapping of PID types to their record classes."""
|
78
|
+
ret: dict[str, type[Record]] = {}
|
79
|
+
for model in self.models.values():
|
80
|
+
pid_type = model.record_pid_type
|
81
|
+
if pid_type is not None:
|
82
|
+
ret[pid_type] = model.record_cls
|
83
|
+
return ret
|
84
|
+
|
85
|
+
@cached_property
|
86
|
+
def draft_class_by_pid_type(self) -> dict[str, type[Draft]]:
|
87
|
+
"""Return a mapping of PID types to their draft classes."""
|
88
|
+
ret: dict[str, type[Draft]] = {}
|
89
|
+
for model in self.models.values():
|
90
|
+
pid_type = model.draft_pid_type
|
91
|
+
if pid_type is not None and model.draft_cls is not None:
|
92
|
+
ret[pid_type] = model.draft_cls
|
93
|
+
return ret
|
94
|
+
|
95
|
+
@cached_property
|
96
|
+
def model_by_pid_type(self) -> dict[str, Model]:
|
97
|
+
"""Return a mapping of PID types to their models."""
|
98
|
+
ret: dict[str, Model] = {}
|
99
|
+
for model in self.models.values():
|
100
|
+
pid_type = model.record_pid_type
|
101
|
+
if pid_type is not None:
|
102
|
+
ret[pid_type] = model
|
103
|
+
pid_type = model.draft_pid_type
|
104
|
+
if pid_type is not None:
|
105
|
+
ret[pid_type] = model
|
106
|
+
return ret
|
107
|
+
|
108
|
+
@cached_property
|
109
|
+
def models_by_schema(self) -> dict[str, Model]:
|
110
|
+
"""Return a mapping of schemas to their models."""
|
111
|
+
ret: dict[str, Model] = {}
|
112
|
+
for model in self.models.values():
|
113
|
+
if model.record_cls is not None:
|
114
|
+
ret[model.record_cls.schema.value] = model # type: ignore # noqa
|
115
|
+
return ret
|
116
|
+
|
117
|
+
@cached_property
|
118
|
+
def rdm_models_by_schema(self) -> dict[str, Model]:
|
119
|
+
"""Return a mapping of RDM schemas to their models."""
|
120
|
+
return {schema: model for schema, model in self.models_by_schema.items() if model.records_alias_enabled}
|
121
|
+
|
122
|
+
def find_pid_type_from_pid(self, pid_value: str) -> str:
|
123
|
+
"""Given a PID value, get its associated PID type.
|
124
|
+
|
125
|
+
This method requires that there are no duplicities in the PID values
|
126
|
+
across models.
|
127
|
+
"""
|
128
|
+
return cast("str", self._filter_model_pid(pid_value=pid_value).pid_type)
|
129
|
+
|
130
|
+
def find_pid_from_uuid(self, uuid: UUID) -> PersistentIdentifier:
|
131
|
+
"""Given an object UUID, get its associated PID."""
|
132
|
+
return self._filter_model_pid(object_uuid=uuid)
|
133
|
+
|
134
|
+
def _filter_model_pid(self, **filter_kwargs: Any) -> PersistentIdentifier:
|
135
|
+
"""Filter PIDs based on the provided criteria and return only one that matches.
|
136
|
+
|
137
|
+
Select persistent identifiers from the DB and return the one that is associated
|
138
|
+
with any service registered within oarepo_runtime. If no such PID exists,
|
139
|
+
an error is raised.
|
140
|
+
|
141
|
+
If the filter matches multiple services, an error is raised.
|
142
|
+
"""
|
143
|
+
pids = PersistentIdentifier.query.filter_by(**filter_kwargs).all()
|
144
|
+
|
145
|
+
filtered_pids = [pid for pid in pids if pid.pid_type in self.record_class_by_pid_type]
|
146
|
+
if not filtered_pids:
|
147
|
+
raise PIDDoesNotExistError(
|
148
|
+
None,
|
149
|
+
filter_kwargs,
|
150
|
+
"The pid value/record uuid is not associated with any record.",
|
151
|
+
)
|
152
|
+
|
153
|
+
if len(filtered_pids) > 1:
|
154
|
+
raise PIDDoesNotExistError(
|
155
|
+
None,
|
156
|
+
filter_kwargs,
|
157
|
+
f"Multiple records found for pid value/record uuid: {filtered_pids}",
|
158
|
+
)
|
159
|
+
return filtered_pids[0]
|
160
|
+
|
64
161
|
@property
|
65
162
|
def services(self) -> dict[str, Service]:
|
66
163
|
"""Return the services registered in the extension."""
|
@@ -82,3 +179,20 @@ class OARepoRuntime:
|
|
82
179
|
model = self.models_by_record_class[t]
|
83
180
|
return model.service
|
84
181
|
raise KeyError(f"No service found for record class '{record_cls.__name__}'.")
|
182
|
+
|
183
|
+
@cached_property
|
184
|
+
def published_indices(self) -> set[str]:
|
185
|
+
"""Return the set of published indices."""
|
186
|
+
indices = set()
|
187
|
+
for model in self.models.values():
|
188
|
+
indices.add(model.record_cls.index.search_alias) # type: ignore[attr-defined]
|
189
|
+
return indices
|
190
|
+
|
191
|
+
@cached_property
|
192
|
+
def draft_indices(self) -> set[str]:
|
193
|
+
"""Return the set of draft indices."""
|
194
|
+
indices = set()
|
195
|
+
for model in self.models.values():
|
196
|
+
if model.draft_cls is not None:
|
197
|
+
indices.add(model.draft_cls.index.search_alias) # type: ignore[attr-defined]
|
198
|
+
return indices
|
@@ -23,7 +23,8 @@ else:
|
|
23
23
|
class UniversalPIDMixin(RecordIdProviderV2):
|
24
24
|
"""Mixin class to handle creation and management of universal PIDs for records."""
|
25
25
|
|
26
|
-
unpid_pid_type = "
|
26
|
+
unpid_pid_type = "recid"
|
27
|
+
"""Setting this to recid so that RDM can use it."""
|
27
28
|
unpid_default_status = PIDStatus.REGISTERED
|
28
29
|
|
29
30
|
@classmethod
|
@@ -1,15 +1,15 @@
|
|
1
|
-
oarepo_runtime/__init__.py,sha256=
|
2
|
-
oarepo_runtime/api.py,sha256=
|
1
|
+
oarepo_runtime/__init__.py,sha256=cUIeG2E4Jh0zema7HEQV6ejcTszVyS1-9iVD0XobEjI,686
|
2
|
+
oarepo_runtime/api.py,sha256=b4v9YTPLK5EsEXzKuEqnymh148GwxCKNpy34kpxWYrI,11842
|
3
3
|
oarepo_runtime/config.py,sha256=RUEPFn_5bKp9Wb0OY-Fb3VK30m35vF5IsLjYaQHhP3g,3838
|
4
|
-
oarepo_runtime/ext.py,sha256=
|
4
|
+
oarepo_runtime/ext.py,sha256=JbbqMM2H0Sq-umN4506wTpAbo5og954u-8MdG0U-ysU,7833
|
5
5
|
oarepo_runtime/proxies.py,sha256=PXaRiBh5qs5-h8M81cJOgtqypFQcYUSjiSn2TLSujRw,648
|
6
6
|
oarepo_runtime/py.typed,sha256=RznSCjXReEUI9zkmD25E8XniG_MvPpLBF6MyNZA8MmE,42
|
7
|
-
oarepo_runtime/cli/__init__.py,sha256=
|
8
|
-
oarepo_runtime/cli/search.py,sha256=
|
7
|
+
oarepo_runtime/cli/__init__.py,sha256=H7GOeOBf0udgKWOdlAQswIMvRrD8BwcEjOVxIqP0Suw,731
|
8
|
+
oarepo_runtime/cli/search.py,sha256=4fHkrjltUUPVUzJiuWaiWxTk62rIYxal3_3jRsZVMmI,1175
|
9
9
|
oarepo_runtime/records/__init__.py,sha256=AbWzmVCY7MhrpdEeI0e3lKzeugPMUSo8T08-NBVeig4,339
|
10
10
|
oarepo_runtime/records/drafts.py,sha256=CS-dUkrylNwscgBGfDyhwGBRCzwsyT6AA3Mhu40ShbY,1607
|
11
11
|
oarepo_runtime/records/mapping.py,sha256=SJbSzerT1645a93-3-Fgz_i3anzFNlrZqbjjwW2ctKs,2660
|
12
|
-
oarepo_runtime/records/pid_providers.py,sha256=
|
12
|
+
oarepo_runtime/records/pid_providers.py,sha256=DSpbVB2Z5GxnjhoY7_WJLfUfrr1UsArJye7CXPfEkNI,1773
|
13
13
|
oarepo_runtime/records/systemfields/__init__.py,sha256=g-u408qyNnsbUTpDtVVwlcyiJaO68GTjDN0W9rXs9pk,524
|
14
14
|
oarepo_runtime/records/systemfields/mapping.py,sha256=66OQavKewJEUMkghymOxvskIO0LUSP2E-MbHryeT5Nk,1968
|
15
15
|
oarepo_runtime/records/systemfields/publication_status.py,sha256=1g3VXNPh0FsiPCpe-7ZuaMEF4x8ffrDrt37Rqnjp0ng,2027
|
@@ -29,8 +29,8 @@ oarepo_runtime/services/records/mapping.py,sha256=y3oeToKEnaRYpMV3q2-2cXNzyzyL3X
|
|
29
29
|
oarepo_runtime/services/schema/__init__.py,sha256=jgAPI_uKC6Ug4KQWnwQVg3-aNaw-eHja323AUFo5ELo,351
|
30
30
|
oarepo_runtime/services/schema/i18n.py,sha256=9D1zOQaPKAnYzejB0vO-m2BJYnam0N0Lrq4jID7twfE,3174
|
31
31
|
oarepo_runtime/services/schema/i18n_ui.py,sha256=DbusphhGDeaobTt4nuwNgKZ6Houlu4Sv3SuMGkdjRRY,3582
|
32
|
-
oarepo_runtime-2.0.0.
|
33
|
-
oarepo_runtime-2.0.0.
|
34
|
-
oarepo_runtime-2.0.0.
|
35
|
-
oarepo_runtime-2.0.0.
|
36
|
-
oarepo_runtime-2.0.0.
|
32
|
+
oarepo_runtime-2.0.0.dev12.dist-info/METADATA,sha256=fmp7MHpzCupK6BCiZoTBCws1V5gqeLsG1Ol6kcEwVgU,4495
|
33
|
+
oarepo_runtime-2.0.0.dev12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
34
|
+
oarepo_runtime-2.0.0.dev12.dist-info/entry_points.txt,sha256=rOfs8R1oXFN_dLH9zAZ6ydkvr83mDajegc6NBIRsCMQ,318
|
35
|
+
oarepo_runtime-2.0.0.dev12.dist-info/licenses/LICENSE,sha256=h2uWz0OaB3EN-J1ImdGJZzc7yvfQjvHVYdUhQ-H7ypY,1064
|
36
|
+
oarepo_runtime-2.0.0.dev12.dist-info/RECORD,,
|
File without changes
|
{oarepo_runtime-2.0.0.dev10.dist-info → oarepo_runtime-2.0.0.dev12.dist-info}/licenses/LICENSE
RENAMED
File without changes
|