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
@@ -1,126 +1,120 @@
|
|
1
|
-
|
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
|
+
"""Link conditions module."""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
|
2
14
|
from abc import abstractmethod
|
3
15
|
from logging import getLogger
|
16
|
+
from typing import Any
|
4
17
|
|
5
18
|
from invenio_pidstore.errors import PIDDoesNotExistError, PIDUnregistered
|
6
|
-
from
|
7
|
-
from invenio_records_resources.records.api import FileRecord, Record
|
19
|
+
from invenio_records_resources.records.api import FileRecord, RecordBase
|
8
20
|
|
9
|
-
from
|
10
|
-
|
11
|
-
get_record_service_for_record,
|
12
|
-
)
|
13
|
-
from ...records.drafts import get_draft
|
21
|
+
from oarepo_runtime.proxies import current_runtime
|
22
|
+
from oarepo_runtime.records.drafts import get_draft
|
14
23
|
|
15
24
|
log = getLogger(__name__)
|
16
25
|
|
17
26
|
|
18
27
|
class Condition:
|
28
|
+
"""Base class for defining conditions with callable logic."""
|
29
|
+
|
19
30
|
@abstractmethod
|
20
|
-
def __call__(self, obj, ctx: dict):
|
21
|
-
|
31
|
+
def __call__(self, obj: RecordBase, ctx: dict):
|
32
|
+
"""Abstract method to be implemented in subclasses to define a condition."""
|
33
|
+
raise NotImplementedError # pragma: no cover
|
22
34
|
|
23
|
-
def __and__(self, other):
|
35
|
+
def __and__(self, other: Any):
|
36
|
+
"""Combine two conditions using a logical AND."""
|
24
37
|
return type(
|
25
|
-
"
|
38
|
+
"And",
|
26
39
|
(Condition,),
|
27
40
|
{"__call__": lambda _, obj, ctx: self(obj, ctx) and other(obj, ctx)},
|
28
41
|
)()
|
29
42
|
|
30
|
-
def __or__(self, other):
|
43
|
+
def __or__(self, other: Any):
|
44
|
+
"""Combine two conditions using a logical OR."""
|
31
45
|
return type(
|
32
|
-
"
|
46
|
+
"Or",
|
33
47
|
(Condition,),
|
34
48
|
{"__call__": lambda _, obj, ctx: self(obj, ctx) or other(obj, ctx)},
|
35
49
|
)()
|
36
50
|
|
37
51
|
|
38
|
-
class
|
39
|
-
"""
|
40
|
-
|
41
|
-
def __call__(self, obj: Record, ctx: dict):
|
42
|
-
return not getattr(obj, "is_draft", False)
|
43
|
-
|
44
|
-
|
45
|
-
class is_draft_record(Condition):
|
46
|
-
"""Shortcut for links to determine if record is a draft record."""
|
47
|
-
|
48
|
-
def __call__(self, obj: Record, ctx: dict):
|
49
|
-
return getattr(obj, "is_draft", False)
|
50
|
-
|
51
|
-
|
52
|
-
class has_draft(Condition):
|
53
|
-
"""Shortcut for links to determine if record is either a draft or a published one with a draft associated."""
|
54
|
-
|
55
|
-
def __call__(self, obj: Record, ctx: dict):
|
56
|
-
if getattr(obj, "is_draft", False):
|
57
|
-
return True
|
58
|
-
if getattr(obj, "has_draft", False):
|
59
|
-
return True
|
60
|
-
return False
|
61
|
-
|
52
|
+
class has_permission(Condition): # noqa: N801
|
53
|
+
"""A condition to check if a user has the specified permission for a given record."""
|
62
54
|
|
63
|
-
|
64
|
-
|
55
|
+
def __init__(self, action_name: str):
|
56
|
+
"""Initialize the condition with the specified action name."""
|
65
57
|
self.action_name = action_name
|
66
58
|
|
67
59
|
def __call__(self, obj: RecordBase, ctx: dict):
|
60
|
+
"""Evaluate the condition by checking the permission for a given record."""
|
68
61
|
if isinstance(obj, FileRecord):
|
69
62
|
obj = obj.record
|
70
|
-
service = get_record_service_for_record(obj)
|
63
|
+
service = current_runtime.get_record_service_for_record(obj)
|
71
64
|
try:
|
72
|
-
return service.check_permission(
|
73
|
-
|
74
|
-
)
|
75
|
-
|
76
|
-
log.exception(f"Unexpected exception {e}.")
|
65
|
+
return service.check_permission(action_name=self.action_name, record=obj, **ctx)
|
66
|
+
except Exception: # pragma: no cover
|
67
|
+
log.exception("Unexpected exception.")
|
68
|
+
|
77
69
|
|
70
|
+
class has_draft_permission(Condition): # noqa: N801
|
71
|
+
"""A condition to check if a user has the specified permission for a draft record."""
|
78
72
|
|
79
|
-
|
80
|
-
|
73
|
+
def __init__(self, action_name: str):
|
74
|
+
"""Initialize the condition with the specified action name."""
|
81
75
|
self.action_name = action_name
|
82
76
|
|
83
77
|
def __call__(self, obj: RecordBase, ctx: dict):
|
78
|
+
"""Valuates the condition by checking the permission for a draft record."""
|
79
|
+
_ = ctx
|
84
80
|
draft_record = get_draft(obj)
|
85
81
|
if not draft_record:
|
86
82
|
return False
|
87
|
-
service = get_record_service_for_record(obj)
|
83
|
+
service = current_runtime.get_record_service_for_record(obj)
|
88
84
|
try:
|
89
|
-
return service.check_permission(
|
90
|
-
|
91
|
-
)
|
92
|
-
except Exception as e:
|
93
|
-
log.exception(f"Unexpected exception {e}.")
|
85
|
+
return service.check_permission(action_name=self.action_name, record=draft_record, **ctx)
|
86
|
+
except Exception: # pragma: no cover
|
87
|
+
log.exception("Unexpected exception.")
|
94
88
|
return False
|
95
89
|
|
96
90
|
|
97
|
-
class
|
98
|
-
|
99
|
-
if isinstance(obj, FileRecord):
|
100
|
-
obj = obj.record
|
101
|
-
service = get_file_service_for_record_class(type(obj))
|
102
|
-
try:
|
103
|
-
return service.check_permission(
|
104
|
-
action_name=self.action_name, record=obj, **ctx
|
105
|
-
)
|
106
|
-
except Exception as e:
|
107
|
-
log.exception(f"Unexpected exception {e}.")
|
91
|
+
class has_draft(Condition): # noqa: N801
|
92
|
+
"""Shortcut for links to determine if record is either a draft or a published one with a draft associated."""
|
108
93
|
|
94
|
+
def __call__(self, obj: RecordBase, ctx: dict):
|
95
|
+
"""Check if the given record has draft."""
|
96
|
+
_ = ctx
|
97
|
+
return bool(getattr(obj, "is_draft", False)) or bool(getattr(obj, "has_draft", False))
|
109
98
|
|
110
|
-
class has_permission_file_service(has_file_permission):
|
111
|
-
def __init__(self, action_name):
|
112
|
-
warnings.warn(
|
113
|
-
"has_permission_file_service is deprecated, use has_file_permission instead",
|
114
|
-
DeprecationWarning,
|
115
|
-
)
|
116
|
-
super().__init__(action_name)
|
117
99
|
|
100
|
+
class has_published_record(Condition): # noqa: N801
|
101
|
+
"""Shortcut for links to determine if the given record has a published PID."""
|
118
102
|
|
119
|
-
|
120
|
-
|
121
|
-
|
103
|
+
def __call__(self, obj: RecordBase, ctx: dict):
|
104
|
+
"""Check if the given record has a published PID."""
|
105
|
+
_ = ctx
|
106
|
+
service = current_runtime.get_record_service_for_record(obj)
|
122
107
|
try:
|
123
108
|
service.record_cls.pid.resolve(obj["id"])
|
124
109
|
except (PIDUnregistered, PIDDoesNotExistError):
|
125
110
|
return False
|
126
111
|
return True
|
112
|
+
|
113
|
+
|
114
|
+
class is_published_record(Condition): # noqa: N801
|
115
|
+
"""Shortcut for links to determine if record is a published record."""
|
116
|
+
|
117
|
+
def __call__(self, obj: RecordBase, ctx: dict):
|
118
|
+
"""Check if the given record is draft."""
|
119
|
+
_ = ctx
|
120
|
+
return not getattr(obj, "is_draft", False)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# mypy: disable-error-code="assignment"
|
2
|
+
#
|
3
|
+
# Copyright (c) 2025 CESNET z.s.p.o.
|
4
|
+
#
|
5
|
+
# This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
|
6
|
+
#
|
7
|
+
# oarepo-runtime is free software; you can redistribute it and/or modify it
|
8
|
+
# under the terms of the MIT License; see LICENSE file for more details.
|
9
|
+
#
|
10
|
+
|
11
|
+
"""Everyone permissions."""
|
12
|
+
|
13
|
+
from __future__ import annotations
|
14
|
+
|
15
|
+
from invenio_records_permissions import RecordPermissionPolicy
|
16
|
+
from invenio_records_permissions.generators import AnyUser, Generator, SystemProcess
|
17
|
+
|
18
|
+
type GeneratorList = tuple[Generator, ...]
|
19
|
+
|
20
|
+
|
21
|
+
class EveryonePermissionPolicy(RecordPermissionPolicy):
|
22
|
+
"""Record policy for read-only repository."""
|
23
|
+
|
24
|
+
can_search: GeneratorList = (SystemProcess(), AnyUser())
|
25
|
+
can_read: GeneratorList = (SystemProcess(), AnyUser())
|
26
|
+
can_create: GeneratorList = (SystemProcess(), AnyUser())
|
27
|
+
can_update: GeneratorList = (SystemProcess(), AnyUser())
|
28
|
+
can_delete: GeneratorList = (SystemProcess(), AnyUser())
|
29
|
+
can_manage: GeneratorList = (SystemProcess(), AnyUser())
|
30
|
+
|
31
|
+
can_create_files: GeneratorList = (SystemProcess(), AnyUser())
|
32
|
+
can_set_content_files: GeneratorList = (SystemProcess(), AnyUser())
|
33
|
+
can_get_content_files: GeneratorList = (SystemProcess(), AnyUser())
|
34
|
+
can_commit_files: GeneratorList = (SystemProcess(), AnyUser())
|
35
|
+
can_read_files: GeneratorList = (SystemProcess(), AnyUser())
|
36
|
+
can_update_files: GeneratorList = (SystemProcess(), AnyUser())
|
37
|
+
can_delete_files: GeneratorList = (SystemProcess(), AnyUser())
|
38
|
+
can_list_files: GeneratorList = (SystemProcess(), AnyUser())
|
39
|
+
can_manage_files: GeneratorList = (SystemProcess(), AnyUser())
|
40
|
+
|
41
|
+
can_edit: GeneratorList = (SystemProcess(), AnyUser())
|
42
|
+
can_new_version: GeneratorList = (SystemProcess(), AnyUser())
|
43
|
+
can_search_drafts: GeneratorList = (SystemProcess(), AnyUser())
|
44
|
+
can_read_draft: GeneratorList = (SystemProcess(), AnyUser())
|
45
|
+
can_search_versions: GeneratorList = (SystemProcess(), AnyUser())
|
46
|
+
can_update_draft: GeneratorList = (SystemProcess(), AnyUser())
|
47
|
+
can_delete_draft: GeneratorList = (SystemProcess(), AnyUser())
|
48
|
+
can_publish: GeneratorList = (SystemProcess(), AnyUser())
|
49
|
+
can_draft_create_files: GeneratorList = (SystemProcess(), AnyUser())
|
50
|
+
can_draft_set_content_files: GeneratorList = (SystemProcess(), AnyUser())
|
51
|
+
can_draft_get_content_files: GeneratorList = (SystemProcess(), AnyUser())
|
52
|
+
can_draft_commit_files: GeneratorList = (SystemProcess(), AnyUser())
|
53
|
+
can_draft_read_files: GeneratorList = (SystemProcess(), AnyUser())
|
54
|
+
can_draft_update_files: GeneratorList = (SystemProcess(), AnyUser())
|
55
|
+
can_draft_delete_files: GeneratorList = (SystemProcess(), AnyUser())
|
56
|
+
|
57
|
+
can_add_community: GeneratorList = (SystemProcess(), AnyUser())
|
58
|
+
can_remove_community: GeneratorList = (SystemProcess(), AnyUser())
|
59
|
+
|
60
|
+
can_read_deleted: GeneratorList = (SystemProcess(), AnyUser())
|
61
|
+
can_manage_record_access: GeneratorList = (SystemProcess(), AnyUser())
|
62
|
+
can_lift_embargo: GeneratorList = (SystemProcess(), AnyUser())
|
@@ -1,33 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
from
|
13
|
-
from .nested_facet import NestedLabeledFacet
|
14
|
-
from .params import FilteredFacetsParam, GroupedFacetsParam
|
15
|
-
from .year_histogram import YearAutoHistogramFacet
|
16
|
-
from .multilingual_facet import MultilingualFacet
|
17
|
-
__all__ = [
|
18
|
-
"LabelledValuesTermsFacet",
|
19
|
-
"DateFacet",
|
20
|
-
"TimeFacet",
|
21
|
-
"DateTimeFacet",
|
22
|
-
"AutoDateHistogramFacet",
|
23
|
-
"EDTFIntervalFacet",
|
24
|
-
"DateIntervalFacet",
|
25
|
-
"EnumTermsFacet",
|
26
|
-
"facet_groups_names",
|
27
|
-
"MaxFacet",
|
28
|
-
"NestedLabeledFacet",
|
29
|
-
"GroupedFacetsParam",
|
30
|
-
"FilteredFacetsParam",
|
31
|
-
"YearAutoHistogramFacet",
|
32
|
-
"MultilingualFacet"
|
33
|
-
]
|
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
|
+
"""Facet parameter classes."""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
@@ -1,55 +1,52 @@
|
|
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
|
+
"""Facet params."""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
|
1
14
|
import copy
|
2
15
|
import logging
|
3
|
-
import
|
4
|
-
from functools import partial, reduce
|
5
|
-
from typing import List
|
16
|
+
from typing import TYPE_CHECKING
|
6
17
|
|
7
18
|
from flask import current_app
|
8
|
-
from flask_principal import Identity
|
9
19
|
from invenio_access.permissions import system_user_id
|
10
20
|
from invenio_app.helpers import obj_or_import_string
|
11
21
|
from invenio_records_resources.services.records.facets import FacetsResponse
|
12
22
|
from invenio_records_resources.services.records.params import FacetsParam
|
13
|
-
from invenio_access.permissions import authenticated_user
|
14
|
-
from invenio_records_resources.services.records.params.base import ParamInterpreter
|
15
|
-
from invenio_search.engine import dsl
|
16
|
-
from invenio_rdm_records.records.systemfields.deletion_status import (
|
17
|
-
RecordDeletionStatusEnum,
|
18
|
-
)
|
19
|
-
|
20
|
-
log = logging.getLogger(__name__)
|
21
|
-
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
return search
|
24
|
+
if TYPE_CHECKING:
|
25
|
+
from flask_principal import Identity
|
26
|
+
from invenio_records_resources.services.records.config import SearchOptions
|
27
|
+
from invenio_search.engine import dsl
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
facet_filter = filters[0]
|
32
|
-
for f in filters[1:]:
|
33
|
-
facet_filter &= f
|
34
|
-
|
35
|
-
return search.filter(facet_filter)
|
29
|
+
log = logging.getLogger(__name__)
|
36
30
|
|
37
31
|
|
38
32
|
class GroupedFacetsParam(FacetsParam):
|
39
|
-
|
33
|
+
"""Facet parameter class that supports grouping of facets."""
|
34
|
+
|
35
|
+
def __init__(self, config: SearchOptions):
|
36
|
+
"""Initialize the facets parameter with the given config."""
|
40
37
|
super().__init__(config)
|
41
38
|
self._facets = {**config.facets}
|
42
39
|
|
43
40
|
@property
|
44
|
-
def facets(self):
|
41
|
+
def facets(self) -> dict[str, dsl.Facet]:
|
42
|
+
"""Return the facets dictionary."""
|
45
43
|
return self._facets
|
46
44
|
|
47
|
-
def identity_facet_groups(self, identity: Identity) ->
|
45
|
+
def identity_facet_groups(self, identity: Identity) -> list[str]:
|
46
|
+
"""Return the facet groups for the given identity."""
|
48
47
|
if "OAREPO_FACET_GROUP_NAME" in current_app.config:
|
49
|
-
find_facet_groups_func = obj_or_import_string(
|
50
|
-
|
51
|
-
)
|
52
|
-
return find_facet_groups_func(identity, self.config, None)
|
48
|
+
find_facet_groups_func = obj_or_import_string(current_app.config["OAREPO_FACET_GROUP_NAME"])
|
49
|
+
return find_facet_groups_func(identity, self.config, None) # type: ignore[no-any-return]
|
53
50
|
|
54
51
|
if hasattr(identity, "provides"):
|
55
52
|
return [need.value for need in identity.provides if need.method == "role"]
|
@@ -57,43 +54,33 @@ class GroupedFacetsParam(FacetsParam):
|
|
57
54
|
return []
|
58
55
|
|
59
56
|
@property
|
60
|
-
def facet_groups(self):
|
57
|
+
def facet_groups(self) -> dict[str, dict[str, dsl.Facet]] | None:
|
58
|
+
"""Return the facet groups defined in the service config."""
|
61
59
|
if hasattr(self.config, "facet_groups"):
|
62
|
-
return self.config.facet_groups
|
60
|
+
return self.config.facet_groups # type: ignore[no-any-return]
|
63
61
|
return None
|
64
62
|
|
65
|
-
def identity_facets(self, identity: Identity):
|
66
|
-
|
67
|
-
for model in current_app.config.get("GLOBAL_SEARCH_MODELS", []):
|
68
|
-
service_config = obj_or_import_string(model["service_config"])
|
69
|
-
if service_config == self.config:
|
70
|
-
global_search_model = True
|
71
|
-
|
63
|
+
def identity_facets(self, identity: Identity) -> dict[str, dsl.Facet]:
|
64
|
+
"""Return the facets for the given identity."""
|
72
65
|
if not self.facet_groups:
|
73
|
-
if global_search_model:
|
74
|
-
log.warning(
|
75
|
-
"No facet groups defined on the service config %s", type(self.config)
|
76
|
-
)
|
77
66
|
return self.facets
|
78
67
|
|
79
68
|
has_system_user_id = identity.id == system_user_id
|
80
|
-
has_system_process_need = any(
|
81
|
-
need.method == "system_process" for need in identity.provides
|
82
|
-
)
|
69
|
+
has_system_process_need = any(need.method == "system_process" for need in identity.provides)
|
83
70
|
if has_system_user_id or has_system_process_need:
|
84
71
|
return self.facets
|
85
72
|
|
86
|
-
|
87
|
-
return user_facets
|
73
|
+
return self._filter_user_facets(identity)
|
88
74
|
|
89
|
-
def
|
75
|
+
def aggregate_with_user_facets(self, search: dsl.Search, user_facets: dict[str, dsl.Facet]) -> dsl.Search:
|
76
|
+
"""Add aggregations representing the user facets."""
|
90
77
|
for name, facet in user_facets.items():
|
91
78
|
agg = facet.get_aggregation()
|
92
79
|
search.aggs.bucket(name, agg)
|
93
80
|
|
94
81
|
return search
|
95
82
|
|
96
|
-
def filter(self, search):
|
83
|
+
def filter(self, search: dsl.Search) -> dsl.Search:
|
97
84
|
"""Apply a post filter on the search."""
|
98
85
|
if not self._filters:
|
99
86
|
return search
|
@@ -106,7 +93,7 @@ class GroupedFacetsParam(FacetsParam):
|
|
106
93
|
|
107
94
|
return search.filter(_filter).post_filter(_filter)
|
108
95
|
|
109
|
-
def apply(self, identity, search, params):
|
96
|
+
def apply(self, identity: Identity, search: dsl.Search, params: dict) -> dsl.Search:
|
110
97
|
"""Evaluate the facets on the search."""
|
111
98
|
facets_values = params.pop("facets", {})
|
112
99
|
for name, values in facets_values.items():
|
@@ -115,17 +102,18 @@ class GroupedFacetsParam(FacetsParam):
|
|
115
102
|
|
116
103
|
user_facets = self.identity_facets(identity)
|
117
104
|
self_copy = copy.copy(self)
|
118
|
-
self_copy._facets = user_facets
|
105
|
+
self_copy._facets = user_facets # noqa: SLF001 - TODO: this looks like a hack
|
119
106
|
search = search.response_class(FacetsResponse.create_response_cls(self_copy))
|
120
107
|
|
121
|
-
search = self.
|
108
|
+
search = self.aggregate_with_user_facets(search, user_facets)
|
122
109
|
search = self.filter(search)
|
123
110
|
|
124
111
|
params.update(self.selected_values)
|
125
112
|
|
126
113
|
return search
|
127
114
|
|
128
|
-
def _filter_user_facets(self, identity: Identity):
|
115
|
+
def _filter_user_facets(self, identity: Identity) -> dict[str, dsl.Facet]:
|
116
|
+
"""Filter user facets based on the identity."""
|
129
117
|
user_facets = {}
|
130
118
|
if not self.facet_groups:
|
131
119
|
user_facets.update(self.facets)
|
@@ -135,58 +123,5 @@ class GroupedFacetsParam(FacetsParam):
|
|
135
123
|
|
136
124
|
groups = self.identity_facet_groups(identity)
|
137
125
|
for group in groups:
|
138
|
-
user_facets.update(self.facet_groups.get(group, {}))
|
126
|
+
user_facets.update((self.facet_groups or {}).get(group, {}))
|
139
127
|
return user_facets
|
140
|
-
|
141
|
-
|
142
|
-
class OARepoAllVersionsParam(ParamInterpreter):
|
143
|
-
"""Evaluates the 'allversions' parameter."""
|
144
|
-
def __init__(self, field_names, config):
|
145
|
-
"""Construct."""
|
146
|
-
self.field_names = field_names
|
147
|
-
super().__init__(config)
|
148
|
-
|
149
|
-
@classmethod
|
150
|
-
def factory(cls, field_names: list[str]):
|
151
|
-
"""Create a new filter parameter."""
|
152
|
-
return partial(cls, field_names)
|
153
|
-
|
154
|
-
def apply(self, identity, search, params):
|
155
|
-
"""Evaluate the allversions parameter on the search."""
|
156
|
-
if not params.get("allversions"):
|
157
|
-
queries = [dsl.query.Q("term", **{field_name: True}) for field_name in self.field_names]
|
158
|
-
query = reduce(operator.or_, queries)
|
159
|
-
search = search.filter(query)
|
160
|
-
return search
|
161
|
-
|
162
|
-
class OARepoPublishedRecordsParam(ParamInterpreter):
|
163
|
-
"""Evaluates the include_deleted parameter."""
|
164
|
-
|
165
|
-
def apply(self, identity, search, params):
|
166
|
-
"""Evaluate the include_deleted parameter on the search."""
|
167
|
-
|
168
|
-
value = params.pop("include_deleted", None)
|
169
|
-
# Filter prevents from displaying deleted records on mainsite search
|
170
|
-
# deleted records should appear only in admins panel
|
171
|
-
if value is None:
|
172
|
-
query = dsl.query.Q(
|
173
|
-
"bool",
|
174
|
-
should=[
|
175
|
-
dsl.query.Q(
|
176
|
-
"bool",
|
177
|
-
must=[
|
178
|
-
dsl.query.Q(
|
179
|
-
"term",
|
180
|
-
deletion_status=RecordDeletionStatusEnum.PUBLISHED.value,
|
181
|
-
)
|
182
|
-
],
|
183
|
-
),
|
184
|
-
# Drafts does not have deletion_status so this clause is needed to
|
185
|
-
# prevent the above clause from filtering out the drafts
|
186
|
-
dsl.query.Q(
|
187
|
-
"bool", must_not=[dsl.query.Q("exists", field="deletion_status")]
|
188
|
-
),
|
189
|
-
],
|
190
|
-
)
|
191
|
-
search = search.filter(query)
|
192
|
-
return search
|
@@ -1,3 +1,16 @@
|
|
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
|
+
"""Service records module."""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
|
1
14
|
from .links import pagination_links_html
|
2
15
|
|
3
|
-
__all__ = ("pagination_links_html",)
|
16
|
+
__all__ = ("pagination_links_html",)
|
@@ -1,21 +1,31 @@
|
|
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
|
+
"""Utility for rendering URI template links."""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
|
1
14
|
from invenio_records_resources.services.base.links import Link
|
2
15
|
|
3
|
-
|
4
|
-
|
16
|
+
|
17
|
+
def pagination_links_html(tpl: str) -> dict[str, Link]:
|
18
|
+
"""Create pagination links (prev/self/next) from the same template."""
|
5
19
|
return {
|
6
20
|
"prev_html": Link(
|
7
21
|
tpl,
|
8
|
-
when=lambda pagination,
|
9
|
-
vars=lambda pagination,
|
10
|
-
{"page": pagination.prev_page.page}
|
11
|
-
),
|
22
|
+
when=lambda pagination, _context: pagination.has_prev,
|
23
|
+
vars=lambda pagination, variables: variables["args"].update({"page": pagination.prev_page.page}),
|
12
24
|
),
|
13
25
|
"self_html": Link(tpl),
|
14
26
|
"next_html": Link(
|
15
27
|
tpl,
|
16
|
-
when=lambda pagination,
|
17
|
-
vars=lambda pagination,
|
18
|
-
{"page": pagination.next_page.page}
|
19
|
-
),
|
28
|
+
when=lambda pagination, _context: pagination.has_next,
|
29
|
+
vars=lambda pagination, variables: variables["args"].update({"page": pagination.next_page.page}),
|
20
30
|
),
|
21
|
-
}
|
31
|
+
}
|
@@ -0,0 +1,42 @@
|
|
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
|
+
"""Update mappings for all record classes in the service registry."""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
from typing import TYPE_CHECKING
|
14
|
+
|
15
|
+
from invenio_records_resources.services.records import (
|
16
|
+
RecordService,
|
17
|
+
RecordServiceConfig,
|
18
|
+
)
|
19
|
+
|
20
|
+
from oarepo_runtime import current_runtime
|
21
|
+
from oarepo_runtime.records.mapping import update_record_system_fields_mapping
|
22
|
+
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from invenio_records_resources.services.base import Service
|
25
|
+
|
26
|
+
|
27
|
+
def update_all_records_mappings() -> None:
|
28
|
+
"""Update all mappings for the registered record classes."""
|
29
|
+
service: Service
|
30
|
+
for service in current_runtime.services.values():
|
31
|
+
if not isinstance(service, RecordService):
|
32
|
+
continue
|
33
|
+
|
34
|
+
config: RecordServiceConfig = service.config
|
35
|
+
|
36
|
+
record_class = getattr(config, "record_cls", None)
|
37
|
+
if record_class:
|
38
|
+
update_record_system_fields_mapping(record_class)
|
39
|
+
|
40
|
+
draft_class = getattr(config, "draft_cls", None)
|
41
|
+
if draft_class:
|
42
|
+
update_record_system_fields_mapping(draft_class)
|