invenio-vocabularies 4.0.0__py2.py3-none-any.whl → 4.2.0__py2.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.

Potentially problematic release.


This version of invenio-vocabularies might be problematic. Click here for more details.

Files changed (76) hide show
  1. invenio_vocabularies/__init__.py +1 -1
  2. invenio_vocabularies/administration/__init__.py +10 -0
  3. invenio_vocabularies/administration/views/__init__.py +10 -0
  4. invenio_vocabularies/administration/views/vocabularies.py +45 -0
  5. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js +8 -20
  6. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.js +2 -2
  7. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingModal.js +5 -7
  8. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/NoAwardResults.js +3 -3
  9. invenio_vocabularies/cli.py +5 -3
  10. invenio_vocabularies/config.py +35 -4
  11. invenio_vocabularies/contrib/affiliations/api.py +1 -2
  12. invenio_vocabularies/contrib/affiliations/config.py +2 -2
  13. invenio_vocabularies/contrib/affiliations/datastreams.py +92 -0
  14. invenio_vocabularies/contrib/affiliations/jsonschemas/affiliations/affiliation-v1.0.0.json +38 -1
  15. invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v1.0.0.json +21 -0
  16. invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v1.0.0.json +21 -0
  17. invenio_vocabularies/contrib/affiliations/mappings/v7/affiliations/affiliation-v1.0.0.json +21 -0
  18. invenio_vocabularies/contrib/affiliations/schema.py +17 -3
  19. invenio_vocabularies/contrib/affiliations/services.py +1 -2
  20. invenio_vocabularies/contrib/awards/awards.py +2 -1
  21. invenio_vocabularies/contrib/awards/datastreams.py +1 -0
  22. invenio_vocabularies/contrib/awards/jsonschemas/awards/award-v1.0.0.json +3 -0
  23. invenio_vocabularies/contrib/awards/mappings/os-v1/awards/award-v1.0.0.json +3 -0
  24. invenio_vocabularies/contrib/awards/mappings/os-v2/awards/award-v1.0.0.json +3 -0
  25. invenio_vocabularies/contrib/awards/mappings/v7/awards/award-v1.0.0.json +3 -0
  26. invenio_vocabularies/contrib/awards/services.py +1 -2
  27. invenio_vocabularies/contrib/common/ror/datastreams.py +140 -6
  28. invenio_vocabularies/contrib/funders/datastreams.py +36 -93
  29. invenio_vocabularies/contrib/funders/funders.py +2 -1
  30. invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json +3 -0
  31. invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json +3 -0
  32. invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json +3 -0
  33. invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json +3 -0
  34. invenio_vocabularies/contrib/funders/serializer.py +2 -1
  35. invenio_vocabularies/contrib/names/jsonschemas/names/name-v1.0.0.json +3 -0
  36. invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v1.0.0.json +3 -0
  37. invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v1.0.0.json +3 -0
  38. invenio_vocabularies/contrib/names/mappings/v7/names/name-v1.0.0.json +3 -0
  39. invenio_vocabularies/contrib/subjects/jsonschemas/subjects/subject-v1.0.0.json +3 -0
  40. invenio_vocabularies/contrib/subjects/mappings/os-v1/subjects/subject-v1.0.0.json +3 -0
  41. invenio_vocabularies/contrib/subjects/mappings/os-v2/subjects/subject-v1.0.0.json +3 -0
  42. invenio_vocabularies/contrib/subjects/mappings/v7/subjects/subject-v1.0.0.json +3 -0
  43. invenio_vocabularies/datastreams/factories.py +1 -2
  44. invenio_vocabularies/datastreams/readers.py +103 -0
  45. invenio_vocabularies/datastreams/tasks.py +25 -0
  46. invenio_vocabularies/datastreams/writers.py +21 -2
  47. invenio_vocabularies/ext.py +22 -7
  48. invenio_vocabularies/factories.py +16 -0
  49. invenio_vocabularies/proxies.py +2 -2
  50. invenio_vocabularies/records/jsonschemas/vocabularies/definitions-v1.0.0.json +7 -0
  51. invenio_vocabularies/records/jsonschemas/vocabularies/vocabulary-v1.0.0.json +1 -4
  52. invenio_vocabularies/records/models.py +2 -4
  53. invenio_vocabularies/records/pidprovider.py +1 -2
  54. invenio_vocabularies/resources/__init__.py +9 -1
  55. invenio_vocabularies/resources/config.py +105 -0
  56. invenio_vocabularies/resources/resource.py +31 -41
  57. invenio_vocabularies/resources/schema.py +2 -1
  58. invenio_vocabularies/services/__init__.py +5 -2
  59. invenio_vocabularies/services/config.py +179 -0
  60. invenio_vocabularies/services/custom_fields/subject.py +2 -1
  61. invenio_vocabularies/services/custom_fields/vocabulary.py +1 -1
  62. invenio_vocabularies/services/permissions.py +3 -1
  63. invenio_vocabularies/services/results.py +110 -0
  64. invenio_vocabularies/services/schema.py +11 -2
  65. invenio_vocabularies/services/service.py +41 -86
  66. invenio_vocabularies/services/tasks.py +1 -31
  67. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabularies-list.html +12 -0
  68. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabulary-details.html +71 -0
  69. invenio_vocabularies/views.py +7 -0
  70. {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.2.0.dist-info}/METADATA +33 -1
  71. {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.2.0.dist-info}/RECORD +76 -66
  72. {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.2.0.dist-info}/entry_points.txt +4 -0
  73. {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.2.0.dist-info}/AUTHORS.rst +0 -0
  74. {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.2.0.dist-info}/LICENSE +0 -0
  75. {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.2.0.dist-info}/WHEEL +0 -0
  76. {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.2.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
3
  # Copyright (C) 2021-2024 CERN.
4
+ # Copyright (C) 2024 University of Münster.
4
5
  #
5
6
  # Invenio-Vocabularies is free software; you can redistribute it and/or
6
7
  # modify it under the terms of the MIT License; see LICENSE file for more
@@ -19,11 +20,17 @@ from json.decoder import JSONDecodeError
19
20
 
20
21
  import requests
21
22
  import yaml
23
+ from lxml import etree
22
24
  from lxml.html import parse as html_parse
23
25
 
24
26
  from .errors import ReaderError
25
27
  from .xml import etree_to_dict
26
28
 
29
+ try:
30
+ import oaipmh_scythe
31
+ except ImportError:
32
+ oaipmh_scythe = None
33
+
27
34
 
28
35
  class BaseReader(ABC):
29
36
  """Base reader."""
@@ -226,3 +233,99 @@ class XMLReader(BaseReader):
226
233
  raise ReaderError(f"Record not found in XML entry.")
227
234
 
228
235
  yield record
236
+
237
+
238
+ class OAIPMHReader(BaseReader):
239
+ """OAIPMH reader."""
240
+
241
+ def __init__(
242
+ self,
243
+ *args,
244
+ base_url=None,
245
+ metadata_prefix=None,
246
+ set=None,
247
+ from_date=None,
248
+ until_date=None,
249
+ verb=None,
250
+ **kwargs,
251
+ ):
252
+ """Constructor."""
253
+ self._base_url = base_url
254
+ self._metadata_prefix = metadata_prefix if not None else "oai_dc"
255
+ self._set = set
256
+ self._until = until_date
257
+ self._from = from_date
258
+ self._verb = verb if not None else "ListRecords"
259
+ super().__init__(*args, **kwargs)
260
+
261
+ def _iter(self, scythe, *args, **kwargs):
262
+ """Read and parse an OAIPMH stream to dict."""
263
+
264
+ class OAIRecord(oaipmh_scythe.models.Record):
265
+ """An XML unpacking implementation for more complicated formats."""
266
+
267
+ def get_metadata(self):
268
+ """Extract and return the record's metadata as a dictionary."""
269
+ return xml_to_dict(
270
+ self.xml.find(f".//{self._oai_namespace}metadata").getchildren()[0],
271
+ )
272
+
273
+ if self._verb == "ListRecords":
274
+ scythe.class_mapping["ListRecords"] = OAIRecord
275
+ try:
276
+ records = scythe.list_records(
277
+ from_=self._from,
278
+ until=self._until,
279
+ metadata_prefix=self._metadata_prefix,
280
+ set_=self._set,
281
+ ignore_deleted=True,
282
+ )
283
+ for record in records:
284
+ yield {"record": record}
285
+ except oaipmh_scythe.NoRecordsMatch:
286
+ raise ReaderError("No records found in OAI-PMH request.")
287
+ else:
288
+ scythe.class_mapping["GetRecord"] = OAIRecord
289
+ try:
290
+ headers = scythe.list_identifiers(
291
+ from_=self._from,
292
+ until=self._until,
293
+ metadata_prefix=self._metadata_prefix,
294
+ set_=self._set,
295
+ ignore_deleted=True,
296
+ )
297
+ for header in headers:
298
+ record = scythe.get_record(
299
+ identifier=header.identifier,
300
+ metadata_prefix=self._metadata_prefix,
301
+ )
302
+ yield {"record": record}
303
+ except oaipmh_scythe.NoRecordsMatch:
304
+ raise ReaderError("No records found in OAI-PMH request.")
305
+
306
+ def read(self, item=None, *args, **kwargs):
307
+ """Reads from item or opens the file descriptor from origin."""
308
+ if item:
309
+ raise NotImplementedError(
310
+ "OAIPMHReader does not support being chained after another reader"
311
+ )
312
+ else:
313
+ with oaipmh_scythe.Scythe(self._base_url) as scythe:
314
+ yield from self._iter(scythe=scythe, *args, **kwargs)
315
+
316
+
317
+ def xml_to_dict(tree: etree._Element):
318
+ """Convert an XML tree to a dictionary.
319
+
320
+ This function takes an XML element tree and converts it into a dictionary.
321
+
322
+ Args:
323
+ tree: The root element of the XML tree to be converted.
324
+
325
+ Returns:
326
+ A dictionary with the key "record".
327
+ """
328
+ dict_obj = dict()
329
+ dict_obj["record"] = etree.tostring(tree)
330
+
331
+ return dict_obj
@@ -0,0 +1,25 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2022-2024 CERN.
4
+ #
5
+ # Invenio-Vocabularies is free software; you can redistribute it and/or
6
+ # modify it under the terms of the MIT License; see LICENSE file for more
7
+ # details.
8
+
9
+ """Data Streams Celery tasks."""
10
+
11
+ from celery import shared_task
12
+
13
+ from ..datastreams import StreamEntry
14
+ from ..datastreams.factories import WriterFactory
15
+
16
+
17
+ @shared_task(ignore_result=True)
18
+ def write_entry(writer, entry):
19
+ """Write an entry.
20
+
21
+ :param writer: writer configuration as accepted by the WriterFactory.
22
+ :param entry: dictionary, StreamEntry is not serializable.
23
+ """
24
+ writer = WriterFactory.create(config=writer)
25
+ writer.write(StreamEntry(entry))
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2021-2022 CERN.
3
+ # Copyright (C) 2021-2024 CERN.
4
4
  #
5
5
  # Invenio-Vocabularies is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the MIT License; see LICENSE file for more
@@ -20,6 +20,7 @@ from marshmallow import ValidationError
20
20
 
21
21
  from .datastreams import StreamEntry
22
22
  from .errors import WriterError
23
+ from .tasks import write_entry
23
24
 
24
25
 
25
26
  class BaseWriter(ABC):
@@ -103,6 +104,24 @@ class YamlWriter(BaseWriter):
103
104
  with open(self._filepath, "a") as file:
104
105
  # made into array for safer append
105
106
  # will always read array (good for reader)
106
- yaml.safe_dump([stream_entry.entry], file)
107
+ yaml.safe_dump([stream_entry.entry], file, allow_unicode=True)
108
+
109
+ return stream_entry
110
+
111
+
112
+ class AsyncWriter(BaseWriter):
113
+ """Writes the entries asynchronously (celery task)."""
114
+
115
+ def __init__(self, writer, *args, **kwargs):
116
+ """Constructor.
117
+
118
+ :param writer: writer to use.
119
+ """
120
+ self._writer = writer
121
+ super().__init__(*args, **kwargs)
122
+
123
+ def write(self, stream_entry, *args, **kwargs):
124
+ """Launches a celery task to write an entry."""
125
+ write_entry.delay(self._writer, stream_entry.entry)
107
126
 
108
127
  return stream_entry
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2020-2022 CERN.
3
+ # Copyright (C) 2020-2024 CERN.
4
4
  # Copyright (C) 2023 Graz University of Technology.
5
5
  #
6
6
  # Invenio-Vocabularies is free software; you can redistribute it and/or
@@ -40,8 +40,14 @@ from .contrib.subjects import (
40
40
  SubjectsService,
41
41
  SubjectsServiceConfig,
42
42
  )
43
- from .resources.resource import VocabulariesResource
44
- from .services.service import VocabulariesService
43
+ from .resources import (
44
+ VocabulariesAdminResource,
45
+ VocabulariesResource,
46
+ VocabulariesResourceConfig,
47
+ VocabularyTypeResourceConfig,
48
+ )
49
+ from .services.config import VocabularyTypesServiceConfig
50
+ from .services.service import VocabulariesService, VocabularyTypeService
45
51
 
46
52
 
47
53
  class InvenioVocabularies(object):
@@ -76,6 +82,7 @@ class InvenioVocabularies(object):
76
82
  funders = FundersServiceConfig
77
83
  names = NamesServiceConfig
78
84
  subjects = SubjectsServiceConfig
85
+ vocabulary_types = VocabularyTypesServiceConfig
79
86
 
80
87
  return ServiceConfigs
81
88
 
@@ -93,9 +100,12 @@ class InvenioVocabularies(object):
93
100
  self.funders_service = FundersService(config=service_configs.funders)
94
101
  self.names_service = NamesService(config=service_configs.names)
95
102
  self.subjects_service = SubjectsService(config=service_configs.subjects)
96
- self.service = VocabulariesService(
103
+ self.vocabularies_service = VocabulariesService(
97
104
  config=app.config["VOCABULARIES_SERVICE_CONFIG"],
98
105
  )
106
+ self.vocabulary_types_service = VocabularyTypeService(
107
+ config=service_configs.vocabulary_types
108
+ )
99
109
 
100
110
  def init_resource(self, app):
101
111
  """Initialize vocabulary resources."""
@@ -121,9 +131,13 @@ class InvenioVocabularies(object):
121
131
  config=SubjectsResourceConfig,
122
132
  )
123
133
  self.resource = VocabulariesResource(
124
- service=self.service,
134
+ service=self.vocabularies_service,
125
135
  config=app.config["VOCABULARIES_RESOURCE_CONFIG"],
126
136
  )
137
+ self.vocabulary_admin_resource = VocabulariesAdminResource(
138
+ service=self.vocabulary_types_service,
139
+ config=VocabularyTypeResourceConfig,
140
+ )
127
141
 
128
142
 
129
143
  def finalize_app(app):
@@ -153,7 +167,8 @@ def init(app):
153
167
  sregistry.register(ext.funders_service, service_id="funders")
154
168
  sregistry.register(ext.names_service, service_id="names")
155
169
  sregistry.register(ext.subjects_service, service_id="subjects")
156
- sregistry.register(ext.service, service_id="vocabularies")
170
+ sregistry.register(ext.vocabularies_service, service_id="vocabularies")
171
+ sregistry.register(ext.vocabulary_types_service, service_id="vocabulary-types")
157
172
  # Register indexers
158
173
  iregistry = app.extensions["invenio-indexer"].registry
159
174
  iregistry.register(ext.affiliations_service.indexer, indexer_id="affiliations")
@@ -161,4 +176,4 @@ def init(app):
161
176
  iregistry.register(ext.funders_service.indexer, indexer_id="funders")
162
177
  iregistry.register(ext.names_service.indexer, indexer_id="names")
163
178
  iregistry.register(ext.subjects_service.indexer, indexer_id="subjects")
164
- iregistry.register(ext.service.indexer, indexer_id="vocabularies")
179
+ iregistry.register(ext.vocabularies_service.indexer, indexer_id="vocabularies")
@@ -7,11 +7,15 @@
7
7
  # modify it under the terms of the MIT License; see LICENSE file for more
8
8
  # details.
9
9
  """Generate Vocabulary Config."""
10
+
10
11
  from copy import deepcopy
11
12
 
12
13
  import yaml
13
14
  from invenio_records_resources.proxies import current_service_registry
14
15
 
16
+ from .contrib.affiliations.datastreams import (
17
+ DATASTREAM_CONFIG as affiliations_ds_config,
18
+ )
15
19
  from .contrib.awards.datastreams import DATASTREAM_CONFIG as awards_ds_config
16
20
  from .contrib.funders.datastreams import DATASTREAM_CONFIG as funders_ds_config
17
21
  from .contrib.names.datastreams import DATASTREAM_CONFIG as names_ds_config
@@ -68,11 +72,23 @@ class AwardsVocabularyConfig(VocabularyConfig):
68
72
  raise NotImplementedError("Service not implemented for Awards")
69
73
 
70
74
 
75
+ class AffiliationsVocabularyConfig(VocabularyConfig):
76
+ """Affiliations Vocabulary Config."""
77
+
78
+ config = affiliations_ds_config
79
+ vocabulary_name = "affiliations"
80
+
81
+ def get_service(self):
82
+ """Get the service for the vocabulary."""
83
+ raise NotImplementedError("Service not implemented for Affiliations")
84
+
85
+
71
86
  def get_vocabulary_config(vocabulary):
72
87
  """Factory function to get the appropriate Vocabulary Config."""
73
88
  vocab_config = {
74
89
  "names": NamesVocabularyConfig,
75
90
  "funders": FundersVocabularyConfig,
76
91
  "awards": AwardsVocabularyConfig,
92
+ "affiliations": AffiliationsVocabularyConfig,
77
93
  }
78
94
  return vocab_config.get(vocabulary, VocabularyConfig)()
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2021 CERN.
3
+ # Copyright (C) 2021-2024 CERN.
4
4
  # Copyright (C) 2021 Northwestern University.
5
5
  #
6
6
  # Invenio-Vocabularies is free software; you can redistribute it and/or
@@ -19,7 +19,7 @@ def _ext_proxy(attr):
19
19
  )
20
20
 
21
21
 
22
- current_service = _ext_proxy("service")
22
+ current_service = _ext_proxy("vocabularies_service")
23
23
  """Proxy to the instantiated vocabulary service."""
24
24
 
25
25
 
@@ -10,5 +10,12 @@
10
10
  },
11
11
  "icon": {
12
12
  "type": "string"
13
+ },
14
+ "tags": {
15
+ "type": "array",
16
+ "description": "Tags for a vocabulary item.",
17
+ "items": {
18
+ "type": "string"
19
+ }
13
20
  }
14
21
  }
@@ -34,10 +34,7 @@
34
34
  }
35
35
  },
36
36
  "tags": {
37
- "type": "array",
38
- "items": {
39
- "type": "string"
40
- }
37
+ "$ref": "local://vocabularies/definitions-v1.0.0.json#/tags"
41
38
  },
42
39
  "title": {
43
40
  "$ref": "local://vocabularies/definitions-v1.0.0.json#/title"
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2020-2021 CERN.
3
+ # Copyright (C) 2020-2024 CERN.
4
4
  #
5
5
  # Invenio-Vocabularies is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the MIT License; see LICENSE file for more
@@ -79,9 +79,7 @@ class VocabularyScheme(db.Model):
79
79
  """Create a new vocabulary subtype."""
80
80
  banned = [",", ":"]
81
81
  for b in banned:
82
- assert (
83
- b not in data["id"]
84
- ), f"No '{b}' allowed in VocabularyScheme.id" # noqa
82
+ assert b not in data["id"], f"No '{b}' allowed in VocabularyScheme.id"
85
83
 
86
84
  with db.session.begin_nested():
87
85
  obj = cls(**data)
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2021 CERN.
3
+ # Copyright (C) 2021-2024 CERN.
4
4
  #
5
5
  # Invenio-Vocabularies is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the MIT License; see LICENSE file for more
@@ -8,7 +8,6 @@
8
8
 
9
9
  """Persistent identifier provider for vocabularies."""
10
10
 
11
-
12
11
  from invenio_pidstore.models import PIDStatus
13
12
  from invenio_pidstore.providers.base import BaseProvider
14
13
 
@@ -1,15 +1,23 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2020-2021 CERN.
3
+ # Copyright (C) 2020-2024 CERN.
4
4
  #
5
5
  # Invenio-Vocabularies is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the MIT License; see LICENSE file for more
7
7
  # details.
8
8
 
9
9
  """Resources module."""
10
+
10
11
  from invenio_vocabularies.resources.schema import L10NString, VocabularyL10Schema
11
12
 
13
+ from .config import VocabulariesResourceConfig, VocabularyTypeResourceConfig
14
+ from .resource import VocabulariesAdminResource, VocabulariesResource
15
+
12
16
  __all__ = (
13
17
  "VocabularyL10Schema",
14
18
  "L10NString",
19
+ "VocabulariesResourceConfig",
20
+ "VocabularyTypeResourceConfig",
21
+ "VocabulariesAdminResource",
22
+ "VocabulariesResource",
15
23
  )
@@ -0,0 +1,105 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2024 CERN.
4
+ # Copyright (C) 2024 University of Münster.
5
+ #
6
+ # Invenio-Vocabularies is free software; you can redistringibute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+
9
+ """Resources config."""
10
+
11
+ import marshmallow as ma
12
+ from flask_resources import (
13
+ BaseListSchema,
14
+ HTTPJSONException,
15
+ JSONSerializer,
16
+ MarshmallowSerializer,
17
+ ResourceConfig,
18
+ ResponseHandler,
19
+ create_error_handler,
20
+ )
21
+ from invenio_records_resources.resources import (
22
+ RecordResource,
23
+ RecordResourceConfig,
24
+ SearchRequestArgsSchema,
25
+ )
26
+ from invenio_records_resources.resources.errors import ErrorHandlersMixin
27
+ from invenio_records_resources.resources.records.args import SearchRequestArgsSchema
28
+ from invenio_records_resources.resources.records.headers import etag_headers
29
+ from invenio_records_resources.services.base.config import ConfiguratorMixin
30
+
31
+ from .serializer import VocabularyL10NItemSchema
32
+
33
+
34
+ class VocabularySearchRequestArgsSchema(SearchRequestArgsSchema):
35
+ """Vocabularies search request parameters."""
36
+
37
+ tags = ma.fields.Str()
38
+ active = ma.fields.Boolean()
39
+ status = ma.fields.Boolean()
40
+
41
+
42
+ class VocabulariesResourceConfig(RecordResourceConfig):
43
+ """Vocabulary resource configuration."""
44
+
45
+ blueprint_name = "vocabularies"
46
+ url_prefix = "/vocabularies"
47
+ routes = {
48
+ "list": "/<type>",
49
+ "item": "/<type>/<pid_value>",
50
+ "tasks": "/tasks",
51
+ }
52
+
53
+ request_view_args = {
54
+ "pid_value": ma.fields.Str(),
55
+ "type": ma.fields.Str(required=True),
56
+ }
57
+
58
+ request_search_args = VocabularySearchRequestArgsSchema
59
+
60
+ response_handlers = {
61
+ "application/json": ResponseHandler(JSONSerializer(), headers=etag_headers),
62
+ "application/vnd.inveniordm.v1+json": ResponseHandler(
63
+ MarshmallowSerializer(
64
+ format_serializer_cls=JSONSerializer,
65
+ object_schema_cls=VocabularyL10NItemSchema,
66
+ list_schema_cls=BaseListSchema,
67
+ ),
68
+ headers=etag_headers,
69
+ ),
70
+ }
71
+
72
+
73
+ class VocabularyTypeResourceConfig(ResourceConfig, ConfiguratorMixin):
74
+ """Vocabulary list resource config."""
75
+
76
+ # /vocabulary/vocabulary_id
77
+ # Blueprint configuration
78
+ blueprint_name = "vocabulary_types"
79
+ url_prefix = "/vocabularies"
80
+
81
+ routes = {"list": "/"}
82
+
83
+ # Request parsing
84
+ request_read_args = {}
85
+ request_view_args = {
86
+ "pid_value": ma.fields.String,
87
+ "type": ma.fields.String,
88
+ }
89
+ request_search_args = VocabularySearchRequestArgsSchema
90
+
91
+ error_handlers = {
92
+ **ErrorHandlersMixin.error_handlers,
93
+ # TODO: Add custom error handlers here
94
+ }
95
+ response_handlers = {
96
+ "application/json": ResponseHandler(JSONSerializer(), headers=etag_headers),
97
+ "application/vnd.inveniordm.v1+json": ResponseHandler(
98
+ MarshmallowSerializer(
99
+ format_serializer_cls=JSONSerializer,
100
+ object_schema_cls=VocabularyL10NItemSchema,
101
+ list_schema_cls=BaseListSchema,
102
+ ),
103
+ headers=etag_headers,
104
+ ),
105
+ }
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2020-2021 CERN.
3
+ # Copyright (C) 2020-2024 CERN.
4
+ # Copyright (C) 2024 Uni Münster.
4
5
  #
5
6
  # Invenio-Vocabularies is free software; you can redistribute it and/or
6
7
  # modify it under the terms of the MIT License; see LICENSE file for more
@@ -8,6 +9,8 @@
8
9
 
9
10
  """Vocabulary resource."""
10
11
 
12
+ import json
13
+
11
14
  import marshmallow as ma
12
15
  from flask import g
13
16
  from flask_resources import (
@@ -18,6 +21,7 @@ from flask_resources import (
18
21
  resource_requestctx,
19
22
  response_handler,
20
23
  )
24
+ from invenio_access.permissions import system_identity
21
25
  from invenio_records_resources.resources import (
22
26
  RecordResource,
23
27
  RecordResourceConfig,
@@ -37,55 +41,20 @@ from marshmallow import fields
37
41
  from .serializer import VocabularyL10NItemSchema
38
42
 
39
43
 
40
- #
41
- # Request args
42
- #
43
- class VocabularySearchRequestArgsSchema(SearchRequestArgsSchema):
44
- """Add parameter to parse tags."""
45
-
46
- tags = fields.Str()
47
-
48
-
49
- #
50
- # Resource config
51
- #
52
- class VocabulariesResourceConfig(RecordResourceConfig):
53
- """Vocabulary resource configuration."""
54
-
55
- blueprint_name = "vocabularies"
56
- url_prefix = "/vocabularies"
57
- routes = {"list": "/<type>", "item": "/<type>/<pid_value>", "tasks": "/tasks"}
58
-
59
- request_view_args = {
60
- "pid_value": ma.fields.Str(),
61
- "type": ma.fields.Str(required=True),
62
- }
63
-
64
- request_search_args = VocabularySearchRequestArgsSchema
65
-
66
- response_handlers = {
67
- "application/json": ResponseHandler(JSONSerializer(), headers=etag_headers),
68
- "application/vnd.inveniordm.v1+json": ResponseHandler(
69
- MarshmallowSerializer(
70
- format_serializer_cls=JSONSerializer,
71
- object_schema_cls=VocabularyL10NItemSchema,
72
- list_schema_cls=BaseListSchema,
73
- ),
74
- headers=etag_headers,
75
- ),
76
- }
77
-
78
-
79
44
  #
80
45
  # Resource
81
46
  #
82
47
  class VocabulariesResource(RecordResource):
83
- """Resource for generic vocabularies."""
48
+ """Resource for generic vocabularies.
49
+
50
+ Provide the API /api/vocabularies/
51
+ """
84
52
 
85
53
  def create_url_rules(self):
86
54
  """Create the URL rules for the record resource."""
87
55
  routes = self.config.routes
88
56
  rules = super().create_url_rules()
57
+
89
58
  rules.append(
90
59
  route("POST", routes["tasks"], self.launch),
91
60
  )
@@ -164,3 +133,24 @@ class VocabulariesResource(RecordResource):
164
133
  """Create a task."""
165
134
  self.service.launch(g.identity, resource_requestctx.data or {})
166
135
  return "", 202
136
+
137
+
138
+ class VocabulariesAdminResource(RecordResource):
139
+ """Resource for vocabularies admin interface."""
140
+
141
+ def create_url_rules(self):
142
+ """Create the URL rules for the record resource."""
143
+ routes = self.config.routes
144
+
145
+ rules = [route("GET", routes["list"], self.search)]
146
+
147
+ return rules
148
+
149
+ @request_search_args
150
+ @response_handler(many=True)
151
+ def search(self):
152
+ """Return information about _all_ vocabularies."""
153
+ identity = g.identity
154
+ hits = self.service.search(identity, params=resource_requestctx.args)
155
+
156
+ return hits.to_dict(), 200
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2020-2021 CERN.
3
+ # Copyright (C) 2020-2024 CERN.
4
4
  # Copyright (C) 2021 Northwestern University.
5
5
  #
6
6
  # Invenio-Vocabularies is free software; you can redistribute it and/or
@@ -8,6 +8,7 @@
8
8
  # details.
9
9
 
10
10
  """Vocabulary resource schema."""
11
+
11
12
  from marshmallow import Schema, fields
12
13
 
13
14
  from invenio_vocabularies.resources.serializer import L10NString
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2020-2021 CERN.
3
+ # Copyright (C) 2020-2024 CERN.
4
4
  #
5
5
  # Invenio-Vocabularies is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the MIT License; see LICENSE file for more
@@ -8,9 +8,12 @@
8
8
 
9
9
  """Services module."""
10
10
 
11
- from .service import VocabulariesService, VocabulariesServiceConfig
11
+ from .config import VocabulariesServiceConfig, VocabularyTypesServiceConfig
12
+ from .service import VocabulariesService, VocabularyTypeService
12
13
 
13
14
  __all__ = (
14
15
  "VocabulariesService",
16
+ "VocabularyTypeService",
15
17
  "VocabulariesServiceConfig",
18
+ "VocabularyTypesServiceConfig",
16
19
  )