oarepo-runtime 1.10.3__py3-none-any.whl → 2.0.0.dev3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. oarepo_runtime/__init__.py +24 -0
  2. oarepo_runtime/api.py +111 -0
  3. oarepo_runtime/cli/__init__.py +10 -21
  4. oarepo_runtime/cli/search.py +34 -0
  5. oarepo_runtime/config.py +86 -13
  6. oarepo_runtime/ext.py +64 -82
  7. oarepo_runtime/proxies.py +21 -5
  8. oarepo_runtime/records/__init__.py +11 -50
  9. oarepo_runtime/records/drafts.py +24 -18
  10. oarepo_runtime/records/mapping.py +84 -0
  11. oarepo_runtime/records/pid_providers.py +43 -7
  12. oarepo_runtime/records/systemfields/__init__.py +15 -33
  13. oarepo_runtime/records/systemfields/mapping.py +41 -24
  14. oarepo_runtime/records/systemfields/publication_status.py +59 -0
  15. oarepo_runtime/services/__init__.py +12 -0
  16. oarepo_runtime/services/config/__init__.py +15 -21
  17. oarepo_runtime/services/config/link_conditions.py +69 -75
  18. oarepo_runtime/services/config/permissions.py +62 -0
  19. oarepo_runtime/services/records/__init__.py +14 -1
  20. oarepo_runtime/services/records/links.py +21 -11
  21. oarepo_runtime/services/records/mapping.py +42 -0
  22. oarepo_runtime/services/results.py +98 -109
  23. oarepo_runtime/services/schema/__init__.py +12 -44
  24. oarepo_runtime/services/schema/i18n.py +47 -22
  25. oarepo_runtime/services/schema/i18n_ui.py +61 -24
  26. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/METADATA +9 -21
  27. oarepo_runtime-2.0.0.dev3.dist-info/RECORD +30 -0
  28. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/WHEEL +1 -2
  29. oarepo_runtime-2.0.0.dev3.dist-info/entry_points.txt +5 -0
  30. oarepo_runtime/cli/assets.py +0 -145
  31. oarepo_runtime/cli/base.py +0 -25
  32. oarepo_runtime/cli/cf.py +0 -15
  33. oarepo_runtime/cli/check.py +0 -167
  34. oarepo_runtime/cli/configuration.py +0 -51
  35. oarepo_runtime/cli/fixtures.py +0 -167
  36. oarepo_runtime/cli/index.py +0 -272
  37. oarepo_runtime/cli/permissions/__init__.py +0 -6
  38. oarepo_runtime/cli/permissions/base.py +0 -26
  39. oarepo_runtime/cli/permissions/evaluate.py +0 -63
  40. oarepo_runtime/cli/permissions/list.py +0 -239
  41. oarepo_runtime/cli/permissions/search.py +0 -121
  42. oarepo_runtime/cli/validate.py +0 -150
  43. oarepo_runtime/datastreams/__init__.py +0 -38
  44. oarepo_runtime/datastreams/asynchronous.py +0 -247
  45. oarepo_runtime/datastreams/catalogue.py +0 -150
  46. oarepo_runtime/datastreams/datastreams.py +0 -152
  47. oarepo_runtime/datastreams/errors.py +0 -54
  48. oarepo_runtime/datastreams/ext.py +0 -41
  49. oarepo_runtime/datastreams/fixtures.py +0 -265
  50. oarepo_runtime/datastreams/json.py +0 -4
  51. oarepo_runtime/datastreams/readers/__init__.py +0 -39
  52. oarepo_runtime/datastreams/readers/attachments.py +0 -51
  53. oarepo_runtime/datastreams/readers/excel.py +0 -123
  54. oarepo_runtime/datastreams/readers/json.py +0 -27
  55. oarepo_runtime/datastreams/readers/service.py +0 -54
  56. oarepo_runtime/datastreams/readers/yaml.py +0 -14
  57. oarepo_runtime/datastreams/semi_asynchronous.py +0 -91
  58. oarepo_runtime/datastreams/synchronous.py +0 -70
  59. oarepo_runtime/datastreams/transformers.py +0 -18
  60. oarepo_runtime/datastreams/types.py +0 -323
  61. oarepo_runtime/datastreams/utils.py +0 -131
  62. oarepo_runtime/datastreams/writers/__init__.py +0 -21
  63. oarepo_runtime/datastreams/writers/attachments_file.py +0 -92
  64. oarepo_runtime/datastreams/writers/attachments_service.py +0 -118
  65. oarepo_runtime/datastreams/writers/publish.py +0 -70
  66. oarepo_runtime/datastreams/writers/service.py +0 -175
  67. oarepo_runtime/datastreams/writers/utils.py +0 -30
  68. oarepo_runtime/datastreams/writers/validation_errors.py +0 -20
  69. oarepo_runtime/datastreams/writers/yaml.py +0 -56
  70. oarepo_runtime/ext_config.py +0 -67
  71. oarepo_runtime/i18n/__init__.py +0 -3
  72. oarepo_runtime/info/__init__.py +0 -0
  73. oarepo_runtime/info/check.py +0 -95
  74. oarepo_runtime/info/permissions/__init__.py +0 -0
  75. oarepo_runtime/info/permissions/debug.py +0 -191
  76. oarepo_runtime/info/views.py +0 -586
  77. oarepo_runtime/profile.py +0 -60
  78. oarepo_runtime/records/dumpers/__init__.py +0 -8
  79. oarepo_runtime/records/dumpers/edtf_interval.py +0 -38
  80. oarepo_runtime/records/dumpers/multilingual_dumper.py +0 -34
  81. oarepo_runtime/records/entity_resolvers/__init__.py +0 -13
  82. oarepo_runtime/records/entity_resolvers/proxies.py +0 -57
  83. oarepo_runtime/records/mappings/__init__.py +0 -0
  84. oarepo_runtime/records/mappings/rdm_parent_mapping.json +0 -483
  85. oarepo_runtime/records/owners/__init__.py +0 -3
  86. oarepo_runtime/records/owners/registry.py +0 -22
  87. oarepo_runtime/records/relations/__init__.py +0 -22
  88. oarepo_runtime/records/relations/base.py +0 -296
  89. oarepo_runtime/records/relations/internal.py +0 -46
  90. oarepo_runtime/records/relations/lookup.py +0 -28
  91. oarepo_runtime/records/relations/pid_relation.py +0 -102
  92. oarepo_runtime/records/systemfields/featured_file.py +0 -45
  93. oarepo_runtime/records/systemfields/has_draftcheck.py +0 -47
  94. oarepo_runtime/records/systemfields/icu.py +0 -371
  95. oarepo_runtime/records/systemfields/owner.py +0 -115
  96. oarepo_runtime/records/systemfields/record_status.py +0 -35
  97. oarepo_runtime/records/systemfields/selectors.py +0 -98
  98. oarepo_runtime/records/systemfields/synthetic.py +0 -130
  99. oarepo_runtime/resources/__init__.py +0 -4
  100. oarepo_runtime/resources/config.py +0 -12
  101. oarepo_runtime/resources/file_resource.py +0 -15
  102. oarepo_runtime/resources/json_serializer.py +0 -27
  103. oarepo_runtime/resources/localized_ui_json_serializer.py +0 -54
  104. oarepo_runtime/resources/resource.py +0 -53
  105. oarepo_runtime/resources/responses.py +0 -20
  106. oarepo_runtime/services/components.py +0 -429
  107. oarepo_runtime/services/config/draft_link.py +0 -23
  108. oarepo_runtime/services/config/permissions_presets.py +0 -174
  109. oarepo_runtime/services/config/service.py +0 -117
  110. oarepo_runtime/services/custom_fields/__init__.py +0 -80
  111. oarepo_runtime/services/custom_fields/mappings.py +0 -188
  112. oarepo_runtime/services/entity/__init__.py +0 -0
  113. oarepo_runtime/services/entity/config.py +0 -14
  114. oarepo_runtime/services/entity/schema.py +0 -9
  115. oarepo_runtime/services/entity/service.py +0 -48
  116. oarepo_runtime/services/expansions/__init__.py +0 -0
  117. oarepo_runtime/services/expansions/expandable_fields.py +0 -21
  118. oarepo_runtime/services/expansions/service.py +0 -4
  119. oarepo_runtime/services/facets/__init__.py +0 -33
  120. oarepo_runtime/services/facets/base.py +0 -12
  121. oarepo_runtime/services/facets/date.py +0 -72
  122. oarepo_runtime/services/facets/enum.py +0 -11
  123. oarepo_runtime/services/facets/facet_groups_names.py +0 -17
  124. oarepo_runtime/services/facets/max_facet.py +0 -13
  125. oarepo_runtime/services/facets/multilingual_facet.py +0 -33
  126. oarepo_runtime/services/facets/nested_facet.py +0 -32
  127. oarepo_runtime/services/facets/params.py +0 -192
  128. oarepo_runtime/services/facets/year_histogram.py +0 -200
  129. oarepo_runtime/services/files/__init__.py +0 -8
  130. oarepo_runtime/services/files/components.py +0 -62
  131. oarepo_runtime/services/files/service.py +0 -16
  132. oarepo_runtime/services/generators.py +0 -10
  133. oarepo_runtime/services/permissions/__init__.py +0 -3
  134. oarepo_runtime/services/permissions/generators.py +0 -103
  135. oarepo_runtime/services/relations/__init__.py +0 -0
  136. oarepo_runtime/services/relations/components.py +0 -15
  137. oarepo_runtime/services/relations/errors.py +0 -18
  138. oarepo_runtime/services/relations/mapping.py +0 -38
  139. oarepo_runtime/services/schema/cf.py +0 -13
  140. oarepo_runtime/services/schema/i18n_validation.py +0 -7
  141. oarepo_runtime/services/schema/marshmallow.py +0 -44
  142. oarepo_runtime/services/schema/marshmallow_to_json_schema.py +0 -72
  143. oarepo_runtime/services/schema/oneofschema.py +0 -192
  144. oarepo_runtime/services/schema/polymorphic.py +0 -21
  145. oarepo_runtime/services/schema/rdm.py +0 -146
  146. oarepo_runtime/services/schema/rdm_ui.py +0 -156
  147. oarepo_runtime/services/schema/ui.py +0 -251
  148. oarepo_runtime/services/schema/validation.py +0 -70
  149. oarepo_runtime/services/search.py +0 -282
  150. oarepo_runtime/services/service.py +0 -61
  151. oarepo_runtime/tasks.py +0 -6
  152. oarepo_runtime/translations/cs/LC_MESSAGES/messages.mo +0 -0
  153. oarepo_runtime/translations/cs/LC_MESSAGES/messages.po +0 -95
  154. oarepo_runtime/translations/default_translations.py +0 -6
  155. oarepo_runtime/translations/en/LC_MESSAGES/messages.mo +0 -0
  156. oarepo_runtime/translations/en/LC_MESSAGES/messages.po +0 -97
  157. oarepo_runtime/translations/messages.pot +0 -100
  158. oarepo_runtime/uow.py +0 -146
  159. oarepo_runtime/utils/__init__.py +0 -0
  160. oarepo_runtime/utils/functools.py +0 -37
  161. oarepo_runtime/utils/identity_utils.py +0 -35
  162. oarepo_runtime/utils/index.py +0 -11
  163. oarepo_runtime/utils/path.py +0 -97
  164. oarepo_runtime-1.10.3.dist-info/RECORD +0 -163
  165. oarepo_runtime-1.10.3.dist-info/entry_points.txt +0 -16
  166. oarepo_runtime-1.10.3.dist-info/top_level.txt +0 -2
  167. tests/marshmallow_to_json/__init__.py +0 -0
  168. tests/marshmallow_to_json/test_datacite_ui_schema.py +0 -1410
  169. tests/marshmallow_to_json/test_simple_schema.py +0 -52
  170. tests/pkg_data/__init__.py +0 -0
  171. {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,24 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """OARepo Runtime package.
11
+
12
+ This package provides support for custom fields identification and iteration and `invenio oarepo cf init`
13
+ initialization tool for customfields.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from .api import Model
19
+ from .ext import OARepoRuntime
20
+ from .proxies import current_runtime
21
+
22
+ __version__ = "2.0.0dev3"
23
+
24
+ __all__ = ("Model", "OARepoRuntime", "__version__", "current_runtime")
oarepo_runtime/api.py ADDED
@@ -0,0 +1,111 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Runtime API classes that are returned from the current_runtime instance."""
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import TYPE_CHECKING, cast
15
+
16
+ from invenio_records_resources.proxies import current_service_registry
17
+
18
+ if TYPE_CHECKING:
19
+ from flask_babel.speaklater import LazyString
20
+ from invenio_drafts_resources.records.api import Draft
21
+ from invenio_records_resources.records.api import RecordBase
22
+ from invenio_records_resources.services import RecordService, RecordServiceConfig
23
+
24
+
25
+ class Model[
26
+ S: RecordService = RecordService,
27
+ C: RecordServiceConfig = RecordServiceConfig,
28
+ R: RecordBase = RecordBase,
29
+ D: Draft = Draft,
30
+ ]:
31
+ """Model configuration.
32
+
33
+ Every model in oarepo repository must have this configuration which must be
34
+ registered in the `oarepo.runtime` extension via the OAREPO_MODELS config
35
+ variable.
36
+ """
37
+
38
+ name: str | LazyString
39
+ version: str
40
+ description: str | LazyString | None = None
41
+ global_search_enabled: bool = False
42
+
43
+ def __init__( # noqa: PLR0913 more attributes as we are creating a config
44
+ self,
45
+ name: str | LazyString,
46
+ version: str,
47
+ service: str | S,
48
+ # params with default values
49
+ service_config: C | None = None,
50
+ description: str | LazyString | None = None,
51
+ record: type[R] | None = None,
52
+ draft: type[D] | None = None,
53
+ global_search_enabled: bool = True,
54
+ ):
55
+ """Initialize the model configuration.
56
+
57
+ :param name: Name of the model, human readable.
58
+ :param version: Version of the model, should be a valid semantic version.
59
+ :param description: Description of the model, human readable.
60
+ :param service: Name of the service inside the `current_service_registry` or
61
+ a configured service instance.
62
+ :param service_config: Service configuration, if not provided,
63
+ if will be taken from the service.
64
+ :param record: Record class, if not provided, it will be taken from the service
65
+ configuration.
66
+ :param draft: Draft class, if not provided, it will be taken from the service
67
+ configuration.
68
+ """
69
+ self.name = name
70
+ self.version = version
71
+ self.description = description
72
+ self.global_search_enabled = global_search_enabled
73
+
74
+ # lazy getters ...
75
+ self._record = record
76
+ self._draft = draft
77
+ self._service = service
78
+ self._service_config = service_config
79
+
80
+ @property
81
+ def service(self) -> S:
82
+ """Get the service."""
83
+ if isinstance(self._service, str):
84
+ return cast(
85
+ "S",
86
+ current_service_registry.get(self._service), # type: ignore[attr-defined]
87
+ )
88
+ return self._service
89
+
90
+ @property
91
+ def service_config(self) -> C:
92
+ """Get the service configuration."""
93
+ if self._service_config is not None:
94
+ return self._service_config
95
+ return cast("C", self.service.config)
96
+
97
+ @property
98
+ def record_cls(self) -> type[R]:
99
+ """Get the record class."""
100
+ if self._record is None:
101
+ return cast("type[R]", self.service.config.record_cls)
102
+ return self._record
103
+
104
+ @property
105
+ def draft_cls(self) -> type[D] | None:
106
+ """Get the draft class."""
107
+ if self._draft is None:
108
+ if hasattr(self.service.config, "draft_cls"):
109
+ return cast("type[D]", self.service.config.draft_cls)
110
+ return None
111
+ return self._draft
@@ -1,22 +1,11 @@
1
- from .assets import assets
2
- from .base import as_command, oarepo
3
- from .cf import cf
4
- from .check import check
5
- from .configuration import configuration_command
6
- from .fixtures import fixtures
7
- from .index import index
8
- from .permissions import permissions
9
- from .validate import validate
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+ """OAREPO Runtime CLI module."""
10
10
 
11
- __all__ = (
12
- "oarepo",
13
- "index",
14
- "as_command",
15
- "assets",
16
- "check",
17
- "validate",
18
- "fixtures",
19
- "configuration_command",
20
- "permissions",
21
- "cf",
22
- )
11
+ from __future__ import annotations
@@ -0,0 +1,34 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+ """OARepo extensions to the index command."""
10
+
11
+ from __future__ import annotations
12
+
13
+ import click
14
+ from flask.cli import with_appcontext
15
+ from invenio_search.cli import index, search_version_check
16
+ from invenio_search.cli import init as original_init
17
+
18
+ from oarepo_runtime.services.records.mapping import update_all_records_mappings
19
+
20
+
21
+ @index.command()
22
+ @click.option("--force", is_flag=True, default=False)
23
+ @with_appcontext
24
+ @search_version_check
25
+ @click.pass_context
26
+ def init(ctx: click.Context, force: bool) -> None:
27
+ """Initialize registered aliases and mappings.
28
+
29
+ This command initializes the search indices by creating templates, component templates,
30
+ index templates, and the actual indices. It will also create all dynamic mappings
31
+ defined inside the models.
32
+ """
33
+ ctx.invoke(original_init, force=force)
34
+ update_all_records_mappings()
oarepo_runtime/config.py CHANGED
@@ -1,19 +1,92 @@
1
- from typing import Any, cast
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
2
9
 
3
- from flask import Flask
10
+ """Config module."""
4
11
 
12
+ from __future__ import annotations
5
13
 
6
- def build_config[T: type](config_class: T, app: Flask, *args: Any, **kwargs: Any) -> T:
7
- """
8
- Builds the configuration for the service
14
+ from typing import TYPE_CHECKING, Any
15
+
16
+ from invenio_vocabularies import __version__ as vocabularies_version
17
+
18
+ from .api import Model
19
+
20
+ if TYPE_CHECKING:
21
+ from collections.abc import Callable
22
+
23
+ from flask import Flask
9
24
 
10
- This function is used to build the configuration for the service
25
+
26
+ def build_config[T](config_class: type[T], app: Flask, *args: Any, **kwargs: Any) -> T:
27
+ """Build the configuration for the service.
28
+
29
+ This function is used to build the configuration for the service.
11
30
  """
12
- if hasattr(config_class, "build") and callable(config_class.build):
31
+ build_config: Callable[[Flask], T] | None = getattr(config_class, "build", None)
32
+ if build_config is not None and callable(build_config):
13
33
  if args or kwargs:
14
- raise ValueError(
15
- "Can not pass extra arguments when invenio ConfigMixin is used"
16
- )
17
- return cast(T, config_class.build(app))
18
- else:
19
- return config_class(*args, **kwargs)
34
+ raise ValueError("Can not pass extra arguments when invenio ConfigMixin is used")
35
+ return build_config(app)
36
+ return config_class(*args, **kwargs)
37
+
38
+
39
+ #
40
+ # Configuration for the extension.
41
+ #
42
+
43
+ OAREPO_MODELS: dict[str, Model] = {
44
+ # default invenio vocabularies
45
+ "vocabularies": Model(
46
+ name="vocabularies",
47
+ version=vocabularies_version,
48
+ service="vocabularies",
49
+ description="Base (non-specialized) invenio vocabularies",
50
+ global_search_enabled=False,
51
+ ),
52
+ # affiliations
53
+ "affiliations": Model(
54
+ name="affiliations",
55
+ version=vocabularies_version,
56
+ service="affiliations",
57
+ description="Affiliations vocabulary",
58
+ global_search_enabled=False,
59
+ ),
60
+ # funders
61
+ "funders": Model(
62
+ name="funders",
63
+ version=vocabularies_version,
64
+ service="funders",
65
+ description="Funders vocabulary",
66
+ global_search_enabled=False,
67
+ ),
68
+ # awards
69
+ "awards": Model(
70
+ name="awards",
71
+ version=vocabularies_version,
72
+ service="awards",
73
+ description="Awards vocabulary",
74
+ global_search_enabled=False,
75
+ ),
76
+ # names
77
+ "names": Model(
78
+ name="names",
79
+ version=vocabularies_version,
80
+ service="names",
81
+ description="Names vocabulary",
82
+ global_search_enabled=False,
83
+ ),
84
+ # subjects
85
+ "subjects": Model(
86
+ name="subjects",
87
+ version=vocabularies_version,
88
+ service="subjects",
89
+ description="Subjects vocabulary",
90
+ global_search_enabled=False,
91
+ ),
92
+ }
oarepo_runtime/ext.py CHANGED
@@ -1,102 +1,84 @@
1
- from functools import cached_property
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Extension preset for runtime module."""
2
11
 
3
- import pytz
4
- from flask import current_app, session
5
- from invenio_accounts.models import User
6
- from invenio_base.utils import obj_or_import_string
12
+ from __future__ import annotations
7
13
 
8
- from .cli import oarepo as oarepo_cmd
9
- from .datastreams.ext import OARepoDataStreamsExt
10
- from .proxies import current_timezone
14
+ from functools import cached_property
15
+ from typing import TYPE_CHECKING, Any, cast
11
16
 
17
+ from flask import current_app
18
+ from invenio_records_resources.proxies import current_service_registry
19
+ from invenio_records_resources.records.api import RecordBase
12
20
 
13
- def set_timezone():
14
- if "timezone" in session:
15
- current_timezone.set(pytz.timezone(session["timezone"]))
16
- else:
17
- default_user_timezone = current_app.config.get("BABEL_DEFAULT_TIMEZONE")
18
- if default_user_timezone:
19
- current_timezone.set(pytz.timezone(default_user_timezone))
20
- else:
21
- current_timezone.set(None)
21
+ from . import config
22
22
 
23
+ if TYPE_CHECKING: # pragma: no cover
24
+ from flask import Flask
25
+ from invenio_records_resources.services.base.service import Service
26
+ from invenio_records_resources.services.records import RecordService
23
27
 
24
- def on_identity_changed(sender, identity):
25
- if "timezone" not in session and "_user_id" in session:
26
- user = User.query.filter_by(id=session["_user_id"]).first()
27
- if user and "timezone" in user.preferences:
28
- session["timezone"] = user.preferences["timezone"]
29
- set_timezone()
28
+ from .api import Model
30
29
 
31
30
 
32
- class OARepoRuntime(object):
33
- """OARepo extension of Invenio-Vocabularies."""
31
+ class OARepoRuntime:
32
+ """OARepo base of invenio oarepo client."""
34
33
 
35
- def __init__(self, app=None):
34
+ def __init__(self, app: Flask | None = None):
36
35
  """Extension initialization."""
37
36
  if app:
38
37
  self.init_app(app)
39
38
 
40
- def init_app(self, app):
39
+ def init_app(self, app: Flask) -> None:
41
40
  """Flask application initialization."""
42
- self.init_config(app)
43
41
  self.app = app
42
+ self.init_config(app)
44
43
  app.extensions["oarepo-runtime"] = self
45
- app.extensions["oarepo-datastreams"] = OARepoDataStreamsExt(app)
46
- app.cli.add_command(oarepo_cmd)
47
-
48
- from flask_principal import identity_changed
49
44
 
50
- identity_changed.connect(on_identity_changed, self.app)
51
- app.before_request(set_timezone)
45
+ def init_config(self, app: Flask) -> None:
46
+ """Initialize the configuration for the extension."""
47
+ app.config.setdefault("OAREPO_MODELS", {})
48
+ for k, v in config.OAREPO_MODELS.items():
49
+ if k not in app.config["OAREPO_MODELS"]:
50
+ app.config["OAREPO_MODELS"][k] = v
52
51
 
53
- @cached_property
54
- def owner_entity_resolvers(self):
55
- return [
56
- obj_or_import_string(x) for x in self.app.config["OWNER_ENTITY_RESOLVERS"]
57
- ]
52
+ @property
53
+ def models(self) -> dict[str, Model]:
54
+ """Return the models registered in the extension."""
55
+ return cast("dict[str, Model]", current_app.config["OAREPO_MODELS"])
58
56
 
59
57
  @cached_property
60
- def rdm_excluded_components(self):
61
- return [
62
- obj_or_import_string(x)
63
- for x in self.app.config.get("RDM_EXCLUDED_COMPONENTS", [])
64
- ]
65
-
66
- def init_config(self, app):
67
- """Initialize configuration."""
68
- from . import ext_config
69
-
70
- if "OAREPO_PERMISSIONS_PRESETS" not in app.config:
71
- app.config["OAREPO_PERMISSIONS_PRESETS"] = {}
72
-
73
- for k in ext_config.OAREPO_PERMISSIONS_PRESETS:
74
- if k not in app.config["OAREPO_PERMISSIONS_PRESETS"]:
75
- app.config["OAREPO_PERMISSIONS_PRESETS"][k] = (
76
- ext_config.OAREPO_PERMISSIONS_PRESETS[k]
77
- )
78
-
79
- for k in dir(ext_config):
80
- if k == "DEFAULT_DATASTREAMS_EXCLUDES":
81
- app.config.setdefault(k, []).extend(getattr(ext_config, k))
82
-
83
- elif k.startswith("DATASTREAMS_"):
84
- val = getattr(ext_config, k)
85
- if isinstance(val, dict):
86
- self.add_non_existing(app.config.setdefault(k, {}), val)
87
- else:
88
- app.config.setdefault(k, val)
89
-
90
- elif k == "HAS_DRAFT_CUSTOM_FIELD":
91
- app.config.setdefault(k, getattr(ext_config, k))
92
-
93
- elif k == "OAREPO_FACET_GROUP_NAME":
94
- app.config.setdefault(k, getattr(ext_config, k))
95
-
96
- elif k == "OWNER_ENTITY_RESOLVERS":
97
- app.config.setdefault(k, []).extend(getattr(ext_config, k))
98
-
99
- def add_non_existing(self, target, source):
100
- for val_k, val_value in source.items():
101
- if val_k not in target:
102
- target[val_k] = val_value
58
+ def models_by_record_class(self) -> dict[type[RecordBase], Model]:
59
+ """Return a mapping of record classes to their models."""
60
+ ret = {model.record_cls: model for model in self.models.values() if model.record_cls is not None}
61
+ ret.update({model.draft_cls: model for model in self.models.values() if model.draft_cls is not None})
62
+ return ret
63
+
64
+ @property
65
+ def services(self) -> dict[str, Service]:
66
+ """Return the services registered in the extension."""
67
+ _services = current_service_registry._services # type: ignore[attr-defined] # noqa: SLF001
68
+ return cast("dict[str, Service]", _services)
69
+
70
+ def get_record_service_for_record(self, record: Any) -> RecordService:
71
+ """Retrieve the associated service for a given record."""
72
+ if record is None:
73
+ raise ValueError("Need to pass a record instance, got None")
74
+ return self.get_record_service_for_record_class(type(record))
75
+
76
+ def get_record_service_for_record_class(self, record_cls: type[RecordBase]) -> RecordService:
77
+ """Retrieve the service associated with a given record class."""
78
+ for t in record_cls.mro():
79
+ if t is RecordBase:
80
+ break
81
+ if t in self.models_by_record_class:
82
+ model = self.models_by_record_class[t]
83
+ return model.service
84
+ raise KeyError(f"No service found for record class '{record_cls.__name__}'.")
oarepo_runtime/proxies.py CHANGED
@@ -1,8 +1,24 @@
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+
10
+ """Proxies."""
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import TYPE_CHECKING
15
+
1
16
  from flask import current_app
2
17
  from werkzeug.local import LocalProxy
3
- from contextvars import ContextVar
4
18
 
5
- current_timezone = ContextVar('timezone') #idk how or exactly why to use the LocalProxy here
6
- current_datastreams = LocalProxy(lambda: current_app.extensions["oarepo-datastreams"])
7
- current_oarepo = LocalProxy(lambda: current_app.extensions["oarepo-runtime"])
8
- """Helper proxy to get the current datastreams."""
19
+ if TYPE_CHECKING:
20
+ from oarepo_runtime.ext import OARepoRuntime
21
+
22
+ current_runtime: OARepoRuntime
23
+
24
+ current_runtime = LocalProxy(lambda: current_app.extensions["oarepo-runtime"]) # type: ignore[assignment]
@@ -1,50 +1,11 @@
1
- from typing import Type
2
-
3
- from deprecated import deprecated
4
- from invenio_records_resources.records import Record
5
-
6
-
7
- def select_record_for_update(record_cls: Type[Record], persistent_identifier):
8
- """Select a record for update."""
9
- resolved_record = record_cls.pid.resolve(persistent_identifier)
10
- model_id = resolved_record.model.id
11
- obj = record_cls.model_cls.query.filter_by(id=model_id).with_for_update().one()
12
- return record_cls(obj.data, model=obj)
13
-
14
-
15
- @deprecated("Moved to oarepo_runtime.services.config.link_conditions")
16
- def is_published_record_function():
17
- """Shortcut for links to determine if record is a published.
18
-
19
- This function is deprecated. Use oarepo_runtime.services.config.is_published_record instead.
20
- """
21
- from oarepo_runtime.services.config.link_conditions import is_published_record
22
-
23
- return is_published_record()
24
-
25
-
26
- @deprecated("Moved to oarepo_runtime.services.config.link_conditions")
27
- def is_draft_record_function():
28
- """Shortcut for links to determine if record is a draft record.
29
-
30
- This function is deprecated. Use oarepo_runtime.services.config.is_draft_record instead.
31
- """
32
- from oarepo_runtime.services.config.link_conditions import is_draft_record
33
-
34
- return is_draft_record()
35
-
36
-
37
- @deprecated("Moved to oarepo_runtime.services.config.link_conditions")
38
- def has_draft_function():
39
- """Shortcut for links to determine if record is either a draft or a published one with a draft associated.
40
-
41
- This function is deprecated. Use oarepo_runtime.services.config.has_draft instead.
42
- """
43
- from oarepo_runtime.services.config.link_conditions import has_draft
44
-
45
- return has_draft()
46
-
47
-
48
- is_published_record = is_published_record_function()
49
- is_draft = is_draft_record_function()
50
- has_draft = has_draft_function()
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+ """Records module."""
10
+
11
+ from __future__ import annotations
@@ -1,13 +1,29 @@
1
- from invenio_records_resources.records.api import Record
1
+ #
2
+ # Copyright (c) 2025 CESNET z.s.p.o.
3
+ #
4
+ # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
5
+ #
6
+ # oarepo-runtime is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+ #
9
+ """Record drafts."""
2
10
 
3
- from oarepo_runtime.datastreams.utils import get_record_service_for_record
11
+ from __future__ import annotations
4
12
 
13
+ from typing import TYPE_CHECKING
5
14
 
6
- def has_draft(record: Record) -> bool:
15
+ if TYPE_CHECKING:
16
+ from invenio_records_resources.records.api import RecordBase
17
+
18
+ from oarepo_runtime.proxies import current_runtime
19
+
20
+
21
+ def has_draft(record: RecordBase) -> bool:
22
+ """Check if record has draft."""
7
23
  return get_draft(record) is not None
8
24
 
9
25
 
10
- def get_draft(record: Record) -> Record | None:
26
+ def get_draft(record: RecordBase) -> RecordBase | None:
11
27
  """Get the draft of a published record, if it exists.
12
28
 
13
29
  A record can have a draft if:
@@ -19,24 +35,14 @@ def get_draft(record: Record) -> Record | None:
19
35
  """
20
36
  if getattr(record, "is_draft", False):
21
37
  return record
22
- if not hasattr(record, "parent"):
23
- return None
24
- if not hasattr(record, "has_draft"):
38
+ if not hasattr(record, "parent") or not hasattr(record, "has_draft"):
25
39
  return None
26
40
 
27
- record_service = get_record_service_for_record(record)
28
- if not record_service:
29
- return None
41
+ record_service = current_runtime.get_record_service_for_record(record)
30
42
 
31
43
  try:
32
- # if there is no record service, we cannot check for draft
33
- if not record_service:
34
- return None
35
- return next(
36
- record_service.config.draft_cls.get_records_by_parent(
37
- record.parent, with_deleted=False
38
- )
39
- )
44
+ parent = getattr(record, "parent", None)
45
+ return next(record_service.config.draft_cls.get_records_by_parent(parent, with_deleted=False))
40
46
  except StopIteration:
41
47
  # no draft found
42
48
  return None