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.
- oarepo_runtime/__init__.py +24 -0
- oarepo_runtime/api.py +111 -0
- oarepo_runtime/cli/__init__.py +10 -21
- oarepo_runtime/cli/search.py +34 -0
- oarepo_runtime/config.py +86 -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 +59 -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/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.dev3.dist-info}/METADATA +9 -21
- oarepo_runtime-2.0.0.dev3.dist-info/RECORD +30 -0
- {oarepo_runtime-1.10.3.dist-info → oarepo_runtime-2.0.0.dev3.dist-info}/WHEEL +1 -2
- oarepo_runtime-2.0.0.dev3.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/__init__.py +0 -33
- 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/params.py +0 -192
- 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.dev3.dist-info}/licenses/LICENSE +0 -0
@@ -1,156 +0,0 @@
|
|
1
|
-
import marshmallow as ma
|
2
|
-
from idutils import to_url
|
3
|
-
from oarepo_vocabularies.services.ui_schema import VocabularyI18nStrUIField
|
4
|
-
|
5
|
-
from oarepo_runtime.services.schema.marshmallow import DictOnlySchema
|
6
|
-
|
7
|
-
from .i18n_ui import MultilingualUIField
|
8
|
-
|
9
|
-
|
10
|
-
class RDMIdentifierWithSchemaUISchema(ma.Schema):
|
11
|
-
scheme = ma.fields.String(
|
12
|
-
required=True,
|
13
|
-
)
|
14
|
-
identifier = ma.fields.String(required=True)
|
15
|
-
|
16
|
-
@ma.post_dump
|
17
|
-
def add_url(self, value, **kwargs):
|
18
|
-
try:
|
19
|
-
# ignore errors here
|
20
|
-
if "identifier" in value and "scheme" in value:
|
21
|
-
url = to_url(
|
22
|
-
value["identifier"], value["scheme"].lower(), url_scheme="https"
|
23
|
-
)
|
24
|
-
if url:
|
25
|
-
value["url"] = url
|
26
|
-
except Exception:
|
27
|
-
pass
|
28
|
-
return value
|
29
|
-
|
30
|
-
|
31
|
-
class RDMAwardIdentifierUISchema(ma.Schema):
|
32
|
-
identifier = ma.fields.String()
|
33
|
-
|
34
|
-
|
35
|
-
class RDMAwardSubjectUISchema(ma.Schema):
|
36
|
-
_id = ma.fields.String(data_key="id")
|
37
|
-
|
38
|
-
subject = ma.fields.String()
|
39
|
-
|
40
|
-
|
41
|
-
class RDMAwardOrganizationUISchema(ma.Schema):
|
42
|
-
schema = ma.fields.String()
|
43
|
-
|
44
|
-
_id = ma.fields.String(data_key="id")
|
45
|
-
|
46
|
-
organization = ma.fields.String()
|
47
|
-
|
48
|
-
|
49
|
-
class RDMFunderVocabularyUISchema(DictOnlySchema):
|
50
|
-
class Meta:
|
51
|
-
unknown = ma.INCLUDE
|
52
|
-
|
53
|
-
_id = ma.fields.String(data_key="id", attribute="id")
|
54
|
-
|
55
|
-
_version = ma.fields.String(data_key="@v", attribute="@v")
|
56
|
-
|
57
|
-
name = ma.fields.String()
|
58
|
-
|
59
|
-
identifiers = ma.fields.List(ma.fields.Nested(RDMIdentifierWithSchemaUISchema()))
|
60
|
-
|
61
|
-
|
62
|
-
class RDMRoleVocabularyUISchema(DictOnlySchema):
|
63
|
-
class Meta:
|
64
|
-
unknown = ma.INCLUDE
|
65
|
-
|
66
|
-
_id = ma.fields.String(data_key="id", attribute="id")
|
67
|
-
|
68
|
-
_version = ma.fields.String(data_key="@v", attribute="@v")
|
69
|
-
|
70
|
-
title = VocabularyI18nStrUIField()
|
71
|
-
|
72
|
-
|
73
|
-
class RDMAwardVocabularyUISchema(DictOnlySchema):
|
74
|
-
class Meta:
|
75
|
-
unknown = ma.INCLUDE
|
76
|
-
|
77
|
-
_id = ma.fields.String(data_key="id", attribute="id")
|
78
|
-
|
79
|
-
_version = ma.fields.String(data_key="@v", attribute="@v")
|
80
|
-
|
81
|
-
title = VocabularyI18nStrUIField()
|
82
|
-
|
83
|
-
number = ma.fields.String()
|
84
|
-
|
85
|
-
identifier = ma.fields.List(ma.fields.Nested(RDMAwardIdentifierUISchema()))
|
86
|
-
|
87
|
-
acronym = ma.fields.String()
|
88
|
-
|
89
|
-
program = ma.fields.String()
|
90
|
-
|
91
|
-
subjects = ma.fields.List(ma.fields.Nested(RDMAwardSubjectUISchema()))
|
92
|
-
|
93
|
-
organizations = ma.fields.List(ma.fields.Nested(RDMAwardOrganizationUISchema()))
|
94
|
-
|
95
|
-
|
96
|
-
class RDMFundersUISchema(ma.Schema):
|
97
|
-
"""Funding ui schema."""
|
98
|
-
|
99
|
-
class Meta:
|
100
|
-
unknown = ma.RAISE
|
101
|
-
|
102
|
-
funder = ma.fields.Nested(lambda: RDMFunderVocabularyUISchema())
|
103
|
-
|
104
|
-
award = ma.fields.Nested(lambda: RDMAwardVocabularyUISchema())
|
105
|
-
|
106
|
-
|
107
|
-
class RDMPersonOrOrganizationUISchema(ma.Schema):
|
108
|
-
class Meta:
|
109
|
-
unknown = ma.INCLUDE
|
110
|
-
|
111
|
-
name = ma.fields.String()
|
112
|
-
|
113
|
-
type = ma.fields.String()
|
114
|
-
|
115
|
-
given_name = ma.fields.String()
|
116
|
-
|
117
|
-
family_name = ma.fields.String()
|
118
|
-
|
119
|
-
identifiers = ma.fields.List(ma.fields.Nested(RDMIdentifierWithSchemaUISchema()))
|
120
|
-
|
121
|
-
|
122
|
-
class RDMAffiliationVocabularyUISchema(DictOnlySchema):
|
123
|
-
class Meta:
|
124
|
-
unknown = ma.INCLUDE
|
125
|
-
|
126
|
-
_id = ma.fields.String(data_key="id", attribute="id")
|
127
|
-
|
128
|
-
_version = ma.fields.String(data_key="@v", attribute="@v")
|
129
|
-
|
130
|
-
name = ma.fields.String()
|
131
|
-
|
132
|
-
|
133
|
-
class RDMCreatorsUISchema(ma.Schema):
|
134
|
-
"""Funding ui schema."""
|
135
|
-
|
136
|
-
class Meta:
|
137
|
-
unknown = ma.RAISE
|
138
|
-
|
139
|
-
role = ma.fields.Nested(lambda: RDMRoleVocabularyUISchema())
|
140
|
-
|
141
|
-
affiliations = ma.fields.List(
|
142
|
-
ma.fields.Nested(lambda: RDMAffiliationVocabularyUISchema())
|
143
|
-
)
|
144
|
-
|
145
|
-
person_or_org = ma.fields.Nested(RDMPersonOrOrganizationUISchema())
|
146
|
-
|
147
|
-
|
148
|
-
class RDMSubjectUISchema(ma.Schema):
|
149
|
-
"""Subject ui schema."""
|
150
|
-
|
151
|
-
class Meta:
|
152
|
-
unknown = ma.RAISE
|
153
|
-
|
154
|
-
_id = ma.fields.String(data_key="id")
|
155
|
-
|
156
|
-
subject = MultilingualUIField()
|
@@ -1,251 +0,0 @@
|
|
1
|
-
import datetime
|
2
|
-
import re
|
3
|
-
|
4
|
-
import marshmallow as ma
|
5
|
-
from babel.dates import format_date
|
6
|
-
from babel_edtf import format_edtf
|
7
|
-
from flask import current_app
|
8
|
-
from idutils import to_url
|
9
|
-
from invenio_rdm_records.records.systemfields.access.field.record import (
|
10
|
-
AccessStatusEnum,
|
11
|
-
)
|
12
|
-
from invenio_rdm_records.resources.serializers.ui.fields import (
|
13
|
-
UIObjectAccessStatus as InvenioUIObjectAccessStatus,
|
14
|
-
)
|
15
|
-
from invenio_rdm_records.services.schemas.parent import RDMParentSchema
|
16
|
-
from invenio_rdm_records.services.schemas.pids import PIDSchema
|
17
|
-
from invenio_rdm_records.services.schemas.record import validate_scheme
|
18
|
-
from invenio_rdm_records.services.schemas.versions import VersionsSchema
|
19
|
-
from marshmallow.fields import Dict, Nested, Raw
|
20
|
-
from marshmallow_utils.fields import (
|
21
|
-
BabelGettextDictField,
|
22
|
-
FormatDate,
|
23
|
-
FormatDatetime,
|
24
|
-
FormatEDTF,
|
25
|
-
FormatTime,
|
26
|
-
SanitizedUnicode,
|
27
|
-
)
|
28
|
-
from marshmallow_utils.fields.babel import BabelFormatField
|
29
|
-
|
30
|
-
from oarepo_runtime.i18n import gettext
|
31
|
-
from oarepo_runtime.i18n import lazy_gettext as _
|
32
|
-
|
33
|
-
from .marshmallow import RDMBaseRecordSchema
|
34
|
-
|
35
|
-
|
36
|
-
def current_default_locale():
|
37
|
-
"""Get the Flask app's default locale."""
|
38
|
-
if current_app:
|
39
|
-
return current_app.config.get("BABEL_DEFAULT_LOCALE", "en")
|
40
|
-
# Use english by default if not specified
|
41
|
-
return "en"
|
42
|
-
|
43
|
-
|
44
|
-
class LocalizedMixin:
|
45
|
-
def __init__(self, *args, locale=None, **kwargs):
|
46
|
-
super().__init__(*args, locale=locale, **kwargs)
|
47
|
-
|
48
|
-
@property
|
49
|
-
def locale(self):
|
50
|
-
if self._locale:
|
51
|
-
return self._locale
|
52
|
-
if self.parent:
|
53
|
-
if "locale" in self.context:
|
54
|
-
return self.context["locale"]
|
55
|
-
return current_default_locale()
|
56
|
-
|
57
|
-
def format_value(self, value):
|
58
|
-
"""Format the value, gracefully handling exceptions."""
|
59
|
-
try:
|
60
|
-
return super().format_value(value)
|
61
|
-
except Exception as e:
|
62
|
-
# Handle the exception gracefully
|
63
|
-
current_app.logger.error(f"Error formatting value '{value}': {e}")
|
64
|
-
return f"«Error formatting value '{value}'»"
|
65
|
-
|
66
|
-
|
67
|
-
# localized date field
|
68
|
-
class LocalizedDate(LocalizedMixin, FormatDate):
|
69
|
-
pass
|
70
|
-
|
71
|
-
|
72
|
-
class FormatTimeString(FormatTime):
|
73
|
-
def parse(self, value, as_time=False, as_date=False, as_datetime=False):
|
74
|
-
if value and isinstance(value, str) and as_time == True:
|
75
|
-
match = re.match(
|
76
|
-
r"^(\d|0\d|1\d|2[0-3]):(\d|[0-5]\d|60)(:(\d|[0-5]\d|60))?$", value
|
77
|
-
)
|
78
|
-
if match:
|
79
|
-
value = datetime.time(
|
80
|
-
hour=int(match.group(1)),
|
81
|
-
minute=int(match.group(2)),
|
82
|
-
second=int(match.group(4)) if match.group(4) else 0,
|
83
|
-
)
|
84
|
-
|
85
|
-
return super().parse(value, as_time, as_date, as_datetime)
|
86
|
-
|
87
|
-
|
88
|
-
class MultilayerFormatEDTF(BabelFormatField):
|
89
|
-
def format_value(self, value):
|
90
|
-
try:
|
91
|
-
return format_date(
|
92
|
-
self.parse(value, as_date=True), format=self._format, locale=self.locale
|
93
|
-
)
|
94
|
-
except:
|
95
|
-
return format_edtf(value, format=self._format, locale=self.locale)
|
96
|
-
|
97
|
-
def parse(self, value, **kwargs):
|
98
|
-
# standard parsing is too lenient, for example returns "2000-01-01" for input "2000"
|
99
|
-
if re.match("^[0-9]+-[0-9]+-[0-9]+", value):
|
100
|
-
return super().parse(value, **kwargs)
|
101
|
-
raise ValueError("Not a valid date")
|
102
|
-
|
103
|
-
class TimezoneMixin: #i'm not sure about where this should be used
|
104
|
-
@property
|
105
|
-
def tzinfo(self):
|
106
|
-
from oarepo_runtime.proxies import current_timezone
|
107
|
-
try:
|
108
|
-
return current_timezone.get()
|
109
|
-
except LookupError:
|
110
|
-
return
|
111
|
-
|
112
|
-
class LocalizedDateTime(TimezoneMixin, LocalizedMixin, FormatDatetime):
|
113
|
-
pass
|
114
|
-
|
115
|
-
class LocalizedTime(LocalizedMixin, FormatTimeString):
|
116
|
-
pass
|
117
|
-
|
118
|
-
|
119
|
-
class LocalizedEDTF(LocalizedMixin, MultilayerFormatEDTF):
|
120
|
-
pass
|
121
|
-
|
122
|
-
|
123
|
-
class LocalizedEDTFTime(LocalizedMixin, MultilayerFormatEDTF):
|
124
|
-
pass
|
125
|
-
|
126
|
-
|
127
|
-
class LocalizedEDTFInterval(LocalizedMixin, FormatEDTF):
|
128
|
-
pass
|
129
|
-
|
130
|
-
|
131
|
-
class LocalizedEDTFTimeInterval(LocalizedMixin, FormatEDTF):
|
132
|
-
pass
|
133
|
-
|
134
|
-
|
135
|
-
class PrefixedGettextField(BabelGettextDictField):
|
136
|
-
def __init__(self, *, value_prefix, locale, default_locale, **kwargs):
|
137
|
-
super().__init__(locale, default_locale, **kwargs)
|
138
|
-
self.value_prefix = value_prefix
|
139
|
-
|
140
|
-
def _serialize(self, value, attr, obj, **kwargs):
|
141
|
-
if value:
|
142
|
-
value = f"{self.value_prefix}{value}"
|
143
|
-
return gettext(value)
|
144
|
-
|
145
|
-
|
146
|
-
class LocalizedEnum(LocalizedMixin, PrefixedGettextField):
|
147
|
-
pass
|
148
|
-
|
149
|
-
def __init__(self, **kwargs):
|
150
|
-
super().__init__(default_locale=current_default_locale, **kwargs)
|
151
|
-
|
152
|
-
|
153
|
-
if False: # NOSONAR
|
154
|
-
# just for the makemessages to pick up the translations
|
155
|
-
translations = [_("True"), _("False")]
|
156
|
-
|
157
|
-
|
158
|
-
class InvenioUISchema(ma.Schema):
|
159
|
-
_schema = ma.fields.Str(attribute="$schema", data_key="$schema")
|
160
|
-
id = ma.fields.Str()
|
161
|
-
created = LocalizedDateTime(dump_only=True)
|
162
|
-
updated = LocalizedDateTime(dump_only=True)
|
163
|
-
links = ma.fields.Raw(dump_only=True)
|
164
|
-
revision_id = ma.fields.Integer(dump_only=True)
|
165
|
-
expanded = ma.fields.Raw(dump_only=True)
|
166
|
-
|
167
|
-
|
168
|
-
# seems not possible to avoid, as they have this hardcoded in their object,
|
169
|
-
# and translation keys are i.e. open, which gets translated to otevret
|
170
|
-
class UIObjectAccessStatus(InvenioUIObjectAccessStatus):
|
171
|
-
@property
|
172
|
-
def title(self):
|
173
|
-
"""Access status title."""
|
174
|
-
return {
|
175
|
-
AccessStatusEnum.OPEN: _("access.status.open"),
|
176
|
-
AccessStatusEnum.EMBARGOED: _("access.status.embargoed"),
|
177
|
-
AccessStatusEnum.RESTRICTED: _("access.status.restricted"),
|
178
|
-
AccessStatusEnum.METADATA_ONLY: _("access.status.metadata-only"),
|
179
|
-
}.get(self.access_status)
|
180
|
-
|
181
|
-
|
182
|
-
class AccessStatusField(ma.fields.Field):
|
183
|
-
"""Record access status."""
|
184
|
-
|
185
|
-
def _serialize(self, value, attr, obj, **kwargs):
|
186
|
-
"""Serialise access status."""
|
187
|
-
record_access_dict = obj.get("access")
|
188
|
-
_files = obj.get("files", {})
|
189
|
-
has_files = _files is not None and _files.get("enabled", False)
|
190
|
-
if record_access_dict:
|
191
|
-
record_access_status_ui = UIObjectAccessStatus(
|
192
|
-
record_access_dict, has_files
|
193
|
-
)
|
194
|
-
return {
|
195
|
-
"id": record_access_status_ui.id,
|
196
|
-
"title_l10n": record_access_status_ui.title,
|
197
|
-
"description_l10n": record_access_status_ui.description,
|
198
|
-
"icon": record_access_status_ui.icon,
|
199
|
-
"embargo_date_l10n": record_access_status_ui.embargo_date,
|
200
|
-
"message_class": record_access_status_ui.message_class,
|
201
|
-
}
|
202
|
-
|
203
|
-
|
204
|
-
# to be able to have access to entire pids object
|
205
|
-
class PIDsField(Dict):
|
206
|
-
"""Custom Dict field for PIDs that adds URLs after serialization."""
|
207
|
-
|
208
|
-
def _serialize(self, value, attr, obj, **kwargs):
|
209
|
-
"""Serialize the PIDs and add URLs to them."""
|
210
|
-
serialized = super()._serialize(value, attr, obj, **kwargs)
|
211
|
-
|
212
|
-
if serialized:
|
213
|
-
for scheme, pid in serialized.items():
|
214
|
-
if scheme and pid and isinstance(pid, dict) and pid.get("identifier"):
|
215
|
-
url = to_url(pid["identifier"], scheme.lower(), url_scheme="https")
|
216
|
-
if url:
|
217
|
-
pid["url"] = url
|
218
|
-
|
219
|
-
return serialized
|
220
|
-
|
221
|
-
|
222
|
-
class InvenioRDMParentUISchema(RDMParentSchema):
|
223
|
-
"""Parent schema."""
|
224
|
-
|
225
|
-
pids = PIDsField(
|
226
|
-
keys=SanitizedUnicode(validate=validate_scheme),
|
227
|
-
values=Nested(PIDSchema),
|
228
|
-
)
|
229
|
-
|
230
|
-
|
231
|
-
class InvenioRDMUISchema(InvenioUISchema, RDMBaseRecordSchema):
|
232
|
-
"""RDM UI schema."""
|
233
|
-
|
234
|
-
is_draft = ma.fields.Boolean(dump_only=True)
|
235
|
-
access_status = AccessStatusField(attribute="access", dump_only=True)
|
236
|
-
versions = ma.fields.Nested(VersionsSchema, dump_only=True)
|
237
|
-
pids = PIDsField(
|
238
|
-
keys=SanitizedUnicode(validate=validate_scheme),
|
239
|
-
values=Nested(PIDSchema),
|
240
|
-
)
|
241
|
-
parent = ma.fields.Nested(InvenioRDMParentUISchema)
|
242
|
-
access = ma.fields.Raw(attribute="access", data_key="access", dump_only=True)
|
243
|
-
files = ma.fields.Raw(attribute="files", data_key="files", dump_only=True)
|
244
|
-
|
245
|
-
def hide_tombstone(self, data):
|
246
|
-
"""Hide tombstone info if the record isn't deleted and metadata if it is."""
|
247
|
-
return data
|
248
|
-
|
249
|
-
def default_nested(self, data):
|
250
|
-
"""Serialize fields as empty dict for partial drafts."""
|
251
|
-
return data
|
@@ -1,70 +0,0 @@
|
|
1
|
-
import functools
|
2
|
-
import re
|
3
|
-
from datetime import datetime
|
4
|
-
from idutils import normalize_pid
|
5
|
-
from isbnlib import canonical, mask
|
6
|
-
|
7
|
-
from marshmallow.exceptions import ValidationError
|
8
|
-
from marshmallow_utils.fields.edtfdatestring import EDTFValidator
|
9
|
-
|
10
|
-
from invenio_i18n import gettext as _
|
11
|
-
|
12
|
-
|
13
|
-
def validate_identifier(value):
|
14
|
-
try:
|
15
|
-
original_identifier = (value["identifier"] or '').strip()
|
16
|
-
normalized_identifier = normalize_pid(
|
17
|
-
value["identifier"], value["scheme"].lower()
|
18
|
-
)
|
19
|
-
if original_identifier and not normalized_identifier:
|
20
|
-
# the normalize_pid library has problems with isbn - does not raise an exception
|
21
|
-
# but returns an empty string
|
22
|
-
raise ValueError()
|
23
|
-
|
24
|
-
# normalized_pid is changing from 10 length ISBN to 13 length ISBN
|
25
|
-
if value["scheme"].lower() == "isbn":
|
26
|
-
canonical_isbn = canonical(value["identifier"])
|
27
|
-
if original_identifier and not canonical_isbn: # just check in case it returns empty string
|
28
|
-
raise ValueError()
|
29
|
-
value["identifier"] = mask(canonical_isbn)
|
30
|
-
return value
|
31
|
-
|
32
|
-
value["identifier"] = normalized_identifier
|
33
|
-
except:
|
34
|
-
raise ValidationError({
|
35
|
-
"identifier": _("Invalid value %(identifier)s of identifier type %(type)s") % {"identifier": value['identifier'], "type": value['scheme']}
|
36
|
-
})
|
37
|
-
return value
|
38
|
-
|
39
|
-
|
40
|
-
def validate_date(date_format):
|
41
|
-
def validate(value):
|
42
|
-
try:
|
43
|
-
datetime.strptime(value, date_format)
|
44
|
-
except Exception as e:
|
45
|
-
raise ValidationError(
|
46
|
-
f"Invalid date/time format, expecting {date_format}, got {value}"
|
47
|
-
) from e
|
48
|
-
|
49
|
-
return validate
|
50
|
-
|
51
|
-
|
52
|
-
def validate_datetime(value):
|
53
|
-
try:
|
54
|
-
datetime.fromisoformat(value)
|
55
|
-
except Exception as e:
|
56
|
-
raise ValidationError(
|
57
|
-
f"Invalid datetime format, expecting iso format, got {value}"
|
58
|
-
) from e
|
59
|
-
|
60
|
-
|
61
|
-
class CachedMultilayerEDTFValidator(EDTFValidator):
|
62
|
-
@functools.lru_cache(maxsize=1024)
|
63
|
-
def __call__(self, value):
|
64
|
-
if re.match(r"^\d{4}$", value):
|
65
|
-
return value
|
66
|
-
try:
|
67
|
-
datetime.strptime(value, "%Y-%m-%d")
|
68
|
-
return value
|
69
|
-
except:
|
70
|
-
return super().__call__(value)
|