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
@@ -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)