oarepo-runtime 2.0.0.dev16__tar.gz → 2.0.0.dev19__tar.gz
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-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/PKG-INFO +1 -1
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/__init__.py +1 -1
- oarepo_runtime-2.0.0.dev19/oarepo_runtime/records/systemfields/custom_fields.py +63 -0
- oarepo_runtime-2.0.0.dev19/oarepo_runtime/records/systemfields/selectors.py +51 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/generators.py +53 -6
- oarepo_runtime-2.0.0.dev19/oarepo_runtime/services/records/custom_fields.py +44 -0
- oarepo_runtime-2.0.0.dev19/oarepo_runtime/typing.py +60 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/.gitignore +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/LICENSE +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/README.md +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/api.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/cli/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/cli/search.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/config.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/ext.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/proxies.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/py.typed +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/drafts.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/mapping.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/pid_providers.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/systemfields/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/systemfields/mapping.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/systemfields/publication_status.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/resources/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/resources/config.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/config/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/config/components.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/config/link_conditions.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/config/permissions.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/facets/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/facets/params.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/records/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/records/links.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/records/mapping.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/results.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/schema/__init__.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/schema/i18n.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/schema/i18n_ui.py +0 -0
- {oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/pyproject.toml +0 -0
@@ -0,0 +1,63 @@
|
|
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
|
+
"""Module to update the mapping of system fields in a record class."""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
import inspect
|
14
|
+
from typing import TYPE_CHECKING
|
15
|
+
|
16
|
+
from flask import current_app
|
17
|
+
from invenio_records.systemfields.relations import MultiRelationsField
|
18
|
+
from invenio_vocabularies.records.systemfields.relations import CustomFieldsRelation
|
19
|
+
|
20
|
+
from oarepo_runtime.records.mapping import prefixed_index, update_record_index
|
21
|
+
|
22
|
+
if TYPE_CHECKING:
|
23
|
+
from collections.abc import Iterable
|
24
|
+
|
25
|
+
from invenio_records.api import RecordBase
|
26
|
+
|
27
|
+
|
28
|
+
def update_record_system_fields_mapping_relation_field(
|
29
|
+
record_class: type[RecordBase],
|
30
|
+
) -> None:
|
31
|
+
"""Update mapping for system fields in the record class.
|
32
|
+
|
33
|
+
:param record_class: The record class which index mapping should be updated.
|
34
|
+
:raise search.RequestError: If there is an error while updating the mapping.
|
35
|
+
"""
|
36
|
+
index = getattr(record_class, "index", None)
|
37
|
+
if not index:
|
38
|
+
return
|
39
|
+
|
40
|
+
for field_name, fld in get_mapping_relation_fields(record_class):
|
41
|
+
custom_fields = current_app.config.get(fld._fields_var, []) # noqa: SLF001
|
42
|
+
|
43
|
+
props: dict[str, dict] = {}
|
44
|
+
mapping = {field_name: {"type": "object", "properties": props}}
|
45
|
+
for cf in custom_fields:
|
46
|
+
# get mapping
|
47
|
+
props[cf.name] = cf.mapping
|
48
|
+
|
49
|
+
# upload mapping
|
50
|
+
if props:
|
51
|
+
update_record_index(prefixed_index(index), {}, mapping, None)
|
52
|
+
|
53
|
+
|
54
|
+
def get_mapping_relation_fields(
|
55
|
+
record_class: type[RecordBase],
|
56
|
+
) -> Iterable[tuple[str, CustomFieldsRelation]]:
|
57
|
+
"""Get all mapping fields from the record class."""
|
58
|
+
for _, relation_fields in inspect.getmembers(record_class, lambda x: isinstance(x, MultiRelationsField)):
|
59
|
+
yield from (
|
60
|
+
(field_name, relation_field)
|
61
|
+
for field_name, relation_field in relation_fields._original_fields.items() # noqa: SLF001
|
62
|
+
if isinstance(relation_field, CustomFieldsRelation)
|
63
|
+
)
|
@@ -0,0 +1,51 @@
|
|
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
|
+
"""Selectors for extracting values from records based on specified paths."""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
from typing import Any, Protocol
|
14
|
+
|
15
|
+
|
16
|
+
class Selector(Protocol):
|
17
|
+
"""Protocol for selectors that extract values from records."""
|
18
|
+
|
19
|
+
def select(self, record: dict) -> list[Any]: # noqa: ARG002
|
20
|
+
"""Select values from the record based on the selector's logic."""
|
21
|
+
return []
|
22
|
+
|
23
|
+
|
24
|
+
class PathSelector(Selector):
|
25
|
+
"""Selector that extracts values from records based on specified paths."""
|
26
|
+
|
27
|
+
def __init__(self, *paths: str) -> None:
|
28
|
+
"""Initialize the PathSelector with given paths."""
|
29
|
+
self.paths = [x.split(".") for x in paths]
|
30
|
+
|
31
|
+
def select(self, record: dict) -> list[Any]:
|
32
|
+
"""Select values from the record based on the specified paths."""
|
33
|
+
ret = []
|
34
|
+
for path in self.paths:
|
35
|
+
ret.extend(list(getter(record, path)))
|
36
|
+
return ret
|
37
|
+
|
38
|
+
|
39
|
+
def getter(data: list | dict, path: list) -> Any:
|
40
|
+
"""Recursively get values from data based on the provided path."""
|
41
|
+
if len(path) == 0:
|
42
|
+
if isinstance(data, list):
|
43
|
+
yield from data
|
44
|
+
else:
|
45
|
+
yield data
|
46
|
+
elif isinstance(data, dict):
|
47
|
+
if path[0] in data:
|
48
|
+
yield from getter(data[path[0]], path[1:])
|
49
|
+
elif isinstance(data, list):
|
50
|
+
for item in data:
|
51
|
+
yield from getter(item, path)
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/generators.py
RENAMED
@@ -10,20 +10,21 @@
|
|
10
10
|
|
11
11
|
from __future__ import annotations
|
12
12
|
|
13
|
-
from abc import abstractmethod
|
13
|
+
from abc import ABC, abstractmethod
|
14
|
+
from itertools import chain
|
14
15
|
from typing import TYPE_CHECKING, Any, override
|
15
16
|
|
16
17
|
from invenio_records_permissions.generators import (
|
17
18
|
ConditionalGenerator as InvenioConditionalGenerator,
|
18
19
|
)
|
19
20
|
from invenio_records_permissions.generators import Generator as InvenioGenerator
|
21
|
+
from invenio_search.engine import dsl
|
20
22
|
|
21
23
|
if TYPE_CHECKING:
|
22
24
|
from collections.abc import Sequence
|
23
25
|
|
24
26
|
from flask_principal import Need
|
25
27
|
from invenio_records_resources.records.api import Record
|
26
|
-
from invenio_search.engine import dsl
|
27
28
|
|
28
29
|
|
29
30
|
class Generator(InvenioGenerator):
|
@@ -45,7 +46,7 @@ class Generator(InvenioGenerator):
|
|
45
46
|
return super().query_filter(**kwargs) # type: ignore[no-any-return]
|
46
47
|
|
47
48
|
|
48
|
-
class ConditionalGenerator(InvenioConditionalGenerator):
|
49
|
+
class ConditionalGenerator(InvenioConditionalGenerator, ABC):
|
49
50
|
"""Typed conditional generator.
|
50
51
|
|
51
52
|
This class will be removed when invenio has proper type stubs.
|
@@ -58,7 +59,7 @@ class ConditionalGenerator(InvenioConditionalGenerator):
|
|
58
59
|
@abstractmethod
|
59
60
|
def _condition(self, **kwargs: Any) -> bool:
|
60
61
|
"""Condition to choose generators set."""
|
61
|
-
raise NotImplementedError # pragma:
|
62
|
+
raise NotImplementedError # pragma: no cover
|
62
63
|
|
63
64
|
def _generators(self, record: Record, **kwargs: Any) -> Sequence[InvenioGenerator]:
|
64
65
|
"""Get the "then" or "else" generators."""
|
@@ -72,6 +73,52 @@ class ConditionalGenerator(InvenioConditionalGenerator):
|
|
72
73
|
def excludes(self, **kwargs: Any) -> Sequence[Need]: # type: ignore[override]
|
73
74
|
return super().excludes(**kwargs) # type: ignore[no-any-return]
|
74
75
|
|
76
|
+
@abstractmethod
|
77
|
+
def _query_instate(self, **context: Any) -> dsl.query.Query:
|
78
|
+
raise NotImplementedError # pragma: no cover
|
79
|
+
|
75
80
|
@override
|
76
|
-
def query_filter(self, **
|
77
|
-
|
81
|
+
def query_filter(self, **context: Any) -> dsl.query.Query: # type: ignore[reportIncompatibleMethodOverride]
|
82
|
+
"""Apply then or else filter."""
|
83
|
+
then_query = super()._make_query(self.then_, **context)
|
84
|
+
else_query = super()._make_query(self.else_, **context)
|
85
|
+
|
86
|
+
q_instate = self._query_instate(**context)
|
87
|
+
q_outstate = ~q_instate
|
88
|
+
|
89
|
+
if then_query and else_query:
|
90
|
+
ret = (q_instate & then_query) | (q_outstate & else_query)
|
91
|
+
elif then_query:
|
92
|
+
ret = q_instate & then_query
|
93
|
+
elif else_query:
|
94
|
+
ret = q_outstate & else_query
|
95
|
+
else:
|
96
|
+
ret = dsl.Q("match_none")
|
97
|
+
|
98
|
+
return ret
|
99
|
+
|
100
|
+
|
101
|
+
class AggregateGenerator(Generator, ABC):
|
102
|
+
"""Superclass for generators aggregating multiple generators."""
|
103
|
+
|
104
|
+
@abstractmethod
|
105
|
+
def _generators(self, **context: Any) -> Sequence[InvenioGenerator]:
|
106
|
+
"""Return the generators."""
|
107
|
+
raise NotImplementedError # pragma: no cover
|
108
|
+
|
109
|
+
@override
|
110
|
+
def needs(self, **context: Any) -> Sequence[Need]:
|
111
|
+
"""Get the needs from the policy."""
|
112
|
+
needs = [generator.needs(**context) for generator in self._generators(**context)]
|
113
|
+
return list(chain.from_iterable(needs))
|
114
|
+
|
115
|
+
@override
|
116
|
+
def excludes(self, **context: Any) -> Sequence[Need]:
|
117
|
+
"""Get the excludes from the policy."""
|
118
|
+
excludes = [generator.excludes(**context) for generator in self._generators(**context)]
|
119
|
+
return list(chain.from_iterable(excludes))
|
120
|
+
|
121
|
+
@override
|
122
|
+
def query_filter(self, **context: Any) -> dsl.query.Query:
|
123
|
+
"""Search filters."""
|
124
|
+
return ConditionalGenerator._make_query(self._generators(**context), **context) # noqa SLF001 # type: ignore[reportReturnType]
|
@@ -0,0 +1,44 @@
|
|
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
|
+
"""Services for updating custom fields mappings in opensearch."""
|
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.systemfields.custom_fields import (
|
22
|
+
update_record_system_fields_mapping_relation_field,
|
23
|
+
)
|
24
|
+
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from invenio_records_resources.services.base import Service
|
27
|
+
|
28
|
+
|
29
|
+
def update_all_records_mappings_relation_fields() -> None:
|
30
|
+
"""Update all mappings for the registered record classes."""
|
31
|
+
service: Service
|
32
|
+
for service in current_runtime.services.values():
|
33
|
+
if not isinstance(service, RecordService):
|
34
|
+
continue
|
35
|
+
|
36
|
+
config: RecordServiceConfig = service.config
|
37
|
+
|
38
|
+
record_class = getattr(config, "record_cls", None)
|
39
|
+
if record_class:
|
40
|
+
update_record_system_fields_mapping_relation_field(record_class)
|
41
|
+
|
42
|
+
draft_class = getattr(config, "draft_cls", None)
|
43
|
+
if draft_class:
|
44
|
+
update_record_system_fields_mapping_relation_field(draft_class)
|
@@ -0,0 +1,60 @@
|
|
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
|
+
"""Module for typing related functionality."""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
|
14
|
+
from typing import TYPE_CHECKING, Any
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from collections.abc import Callable
|
18
|
+
|
19
|
+
|
20
|
+
def require_kwargs(*kwargs_names: str) -> Any:
|
21
|
+
"""Wrap function to require specific kwargs in a function call.
|
22
|
+
|
23
|
+
This decorator is used to fix typing errors in inherited classes where the base class defines kwargs and the
|
24
|
+
inherited class needs to access a specific kwarg.
|
25
|
+
|
26
|
+
Example:
|
27
|
+
```python
|
28
|
+
# base class
|
29
|
+
class ConditionalGenerator(
|
30
|
+
InvenioConditionalGenerator, ABC
|
31
|
+
):
|
32
|
+
@abstractmethod
|
33
|
+
def _condition(
|
34
|
+
self, **kwargs: Any
|
35
|
+
) -> bool: ...
|
36
|
+
|
37
|
+
|
38
|
+
# inherited class
|
39
|
+
class IfRecordHasField(
|
40
|
+
ConditionalGenerator
|
41
|
+
):
|
42
|
+
@override
|
43
|
+
@require_kwargs("field")
|
44
|
+
def _condition(
|
45
|
+
self, *, field, **kwargs: Any
|
46
|
+
) -> bool: ...
|
47
|
+
```
|
48
|
+
|
49
|
+
"""
|
50
|
+
|
51
|
+
def wrapper(f: Callable) -> Callable:
|
52
|
+
def wrapped_f(*args: Any, **kwargs: Any) -> Any:
|
53
|
+
for kwarg_name in kwargs_names:
|
54
|
+
if kwarg_name not in kwargs:
|
55
|
+
raise ValueError(f"Keyword argument {kwarg_name} not found in function call.")
|
56
|
+
return f(*args, **kwargs)
|
57
|
+
|
58
|
+
return wrapped_f
|
59
|
+
|
60
|
+
return wrapper
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/records/pid_providers.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/resources/__init__.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/resources/config.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/__init__.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/config/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/facets/__init__.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/facets/params.py
RENAMED
File without changes
|
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/records/links.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/records/mapping.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/results.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/schema/__init__.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/schema/i18n.py
RENAMED
File without changes
|
{oarepo_runtime-2.0.0.dev16 → oarepo_runtime-2.0.0.dev19}/oarepo_runtime/services/schema/i18n_ui.py
RENAMED
File without changes
|
File without changes
|