invenio-vocabularies 2.3.1__py2.py3-none-any.whl → 6.3.1__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 (165) hide show
  1. invenio_vocabularies/__init__.py +2 -2
  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/package.json +1 -7
  6. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js +80 -64
  7. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.js +49 -41
  8. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingModal.js +5 -7
  9. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/NoAwardResults.js +3 -3
  10. invenio_vocabularies/cli.py +31 -44
  11. invenio_vocabularies/config.py +68 -4
  12. invenio_vocabularies/contrib/affiliations/affiliations.py +11 -0
  13. invenio_vocabularies/contrib/affiliations/api.py +1 -2
  14. invenio_vocabularies/contrib/affiliations/config.py +13 -2
  15. invenio_vocabularies/contrib/affiliations/datastreams.py +186 -0
  16. invenio_vocabularies/contrib/affiliations/jsonschemas/affiliations/affiliation-v1.0.0.json +38 -1
  17. invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v1.0.0.json +22 -1
  18. invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v2.0.0.json +171 -0
  19. invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v1.0.0.json +22 -1
  20. invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v2.0.0.json +171 -0
  21. invenio_vocabularies/contrib/affiliations/mappings/v7/affiliations/affiliation-v1.0.0.json +22 -1
  22. invenio_vocabularies/contrib/affiliations/schema.py +17 -3
  23. invenio_vocabularies/contrib/affiliations/services.py +1 -2
  24. invenio_vocabularies/contrib/awards/awards.py +17 -5
  25. invenio_vocabularies/contrib/awards/datastreams.py +241 -7
  26. invenio_vocabularies/contrib/awards/jsonschemas/awards/award-v1.0.0.json +38 -0
  27. invenio_vocabularies/contrib/awards/mappings/os-v1/awards/award-v1.0.0.json +51 -2
  28. invenio_vocabularies/contrib/awards/mappings/os-v2/awards/award-v1.0.0.json +51 -2
  29. invenio_vocabularies/contrib/awards/mappings/v7/awards/award-v1.0.0.json +51 -2
  30. invenio_vocabularies/contrib/awards/schema.py +16 -1
  31. invenio_vocabularies/contrib/awards/serializer.py +8 -1
  32. invenio_vocabularies/contrib/awards/services.py +1 -2
  33. invenio_vocabularies/contrib/common/__init__.py +9 -0
  34. invenio_vocabularies/contrib/common/openaire/__init__.py +9 -0
  35. invenio_vocabularies/contrib/common/openaire/datastreams.py +84 -0
  36. invenio_vocabularies/contrib/common/ror/__init__.py +9 -0
  37. invenio_vocabularies/contrib/common/ror/datastreams.py +220 -0
  38. invenio_vocabularies/contrib/funders/config.py +11 -2
  39. invenio_vocabularies/contrib/funders/datastreams.py +40 -62
  40. invenio_vocabularies/contrib/funders/funders.py +3 -1
  41. invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json +36 -1
  42. invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json +22 -1
  43. invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v2.0.0.json +156 -0
  44. invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json +22 -1
  45. invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v2.0.0.json +156 -0
  46. invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json +22 -1
  47. invenio_vocabularies/contrib/funders/schema.py +8 -0
  48. invenio_vocabularies/contrib/funders/serializer.py +2 -1
  49. invenio_vocabularies/contrib/names/config.py +5 -3
  50. invenio_vocabularies/contrib/names/datastreams.py +172 -4
  51. invenio_vocabularies/contrib/names/jsonschemas/names/name-v1.0.0.json +3 -0
  52. invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v1.0.0.json +3 -0
  53. invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v2.0.0.json +150 -0
  54. invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v1.0.0.json +3 -0
  55. invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v2.0.0.json +150 -0
  56. invenio_vocabularies/contrib/names/mappings/v7/names/name-v1.0.0.json +3 -0
  57. invenio_vocabularies/contrib/names/names.py +15 -3
  58. invenio_vocabularies/contrib/names/permissions.py +20 -0
  59. invenio_vocabularies/contrib/names/s3client.py +44 -0
  60. invenio_vocabularies/contrib/names/schema.py +14 -0
  61. invenio_vocabularies/contrib/subjects/config.py +9 -3
  62. invenio_vocabularies/contrib/subjects/datastreams.py +61 -0
  63. invenio_vocabularies/contrib/subjects/euroscivoc/__init__.py +9 -0
  64. invenio_vocabularies/contrib/subjects/euroscivoc/datastreams.py +171 -0
  65. invenio_vocabularies/contrib/subjects/jsonschemas/subjects/subject-v1.0.0.json +31 -0
  66. invenio_vocabularies/contrib/subjects/mappings/os-v1/subjects/subject-v1.0.0.json +35 -0
  67. invenio_vocabularies/contrib/subjects/mappings/os-v2/subjects/subject-v1.0.0.json +35 -0
  68. invenio_vocabularies/contrib/subjects/mappings/v7/subjects/subject-v1.0.0.json +35 -0
  69. invenio_vocabularies/contrib/subjects/mesh/__init__.py +9 -0
  70. invenio_vocabularies/contrib/subjects/mesh/datastreams.py +43 -0
  71. invenio_vocabularies/contrib/subjects/schema.py +47 -5
  72. invenio_vocabularies/contrib/subjects/subjects.py +10 -0
  73. invenio_vocabularies/datastreams/datastreams.py +61 -13
  74. invenio_vocabularies/datastreams/factories.py +1 -2
  75. invenio_vocabularies/datastreams/readers.py +138 -29
  76. invenio_vocabularies/datastreams/tasks.py +37 -0
  77. invenio_vocabularies/datastreams/transformers.py +17 -27
  78. invenio_vocabularies/datastreams/writers.py +116 -14
  79. invenio_vocabularies/datastreams/xml.py +34 -0
  80. invenio_vocabularies/ext.py +59 -5
  81. invenio_vocabularies/factories.py +137 -0
  82. invenio_vocabularies/jobs.py +133 -0
  83. invenio_vocabularies/proxies.py +2 -2
  84. invenio_vocabularies/records/jsonschemas/vocabularies/definitions-v1.0.0.json +7 -0
  85. invenio_vocabularies/records/jsonschemas/vocabularies/vocabulary-v1.0.0.json +1 -4
  86. invenio_vocabularies/records/mappings/os-v1/vocabularies/vocabulary-v1.0.0.json +3 -3
  87. invenio_vocabularies/records/mappings/os-v2/vocabularies/vocabulary-v1.0.0.json +3 -3
  88. invenio_vocabularies/records/mappings/v7/vocabularies/vocabulary-v1.0.0.json +3 -3
  89. invenio_vocabularies/records/models.py +2 -4
  90. invenio_vocabularies/records/pidprovider.py +1 -2
  91. invenio_vocabularies/records/systemfields/relations.py +2 -2
  92. invenio_vocabularies/resources/__init__.py +9 -1
  93. invenio_vocabularies/resources/config.py +105 -0
  94. invenio_vocabularies/resources/resource.py +31 -41
  95. invenio_vocabularies/resources/schema.py +2 -1
  96. invenio_vocabularies/services/__init__.py +5 -2
  97. invenio_vocabularies/services/config.py +179 -0
  98. invenio_vocabularies/services/custom_fields/__init__.py +6 -2
  99. invenio_vocabularies/services/custom_fields/subject.py +82 -0
  100. invenio_vocabularies/services/custom_fields/vocabulary.py +5 -3
  101. invenio_vocabularies/services/permissions.py +3 -1
  102. invenio_vocabularies/services/results.py +110 -0
  103. invenio_vocabularies/services/schema.py +11 -2
  104. invenio_vocabularies/services/service.py +46 -94
  105. invenio_vocabularies/services/tasks.py +1 -1
  106. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/subjects.html +23 -0
  107. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabularies-list.html +12 -0
  108. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabulary-details.html +71 -0
  109. invenio_vocabularies/translations/af/LC_MESSAGES/messages.mo +0 -0
  110. invenio_vocabularies/translations/ar/LC_MESSAGES/messages.mo +0 -0
  111. invenio_vocabularies/translations/bg/LC_MESSAGES/messages.mo +0 -0
  112. invenio_vocabularies/translations/ca/LC_MESSAGES/messages.mo +0 -0
  113. invenio_vocabularies/translations/cs/LC_MESSAGES/messages.mo +0 -0
  114. invenio_vocabularies/translations/da/LC_MESSAGES/messages.mo +0 -0
  115. invenio_vocabularies/translations/de/LC_MESSAGES/messages.mo +0 -0
  116. invenio_vocabularies/translations/de_AT/LC_MESSAGES/messages.mo +0 -0
  117. invenio_vocabularies/translations/de_DE/LC_MESSAGES/messages.mo +0 -0
  118. invenio_vocabularies/translations/el/LC_MESSAGES/messages.mo +0 -0
  119. invenio_vocabularies/translations/en/LC_MESSAGES/messages.mo +0 -0
  120. invenio_vocabularies/translations/en_AT/LC_MESSAGES/messages.mo +0 -0
  121. invenio_vocabularies/translations/en_HU/LC_MESSAGES/messages.mo +0 -0
  122. invenio_vocabularies/translations/es/LC_MESSAGES/messages.mo +0 -0
  123. invenio_vocabularies/translations/es_CU/LC_MESSAGES/messages.mo +0 -0
  124. invenio_vocabularies/translations/es_MX/LC_MESSAGES/messages.mo +0 -0
  125. invenio_vocabularies/translations/et/LC_MESSAGES/messages.mo +0 -0
  126. invenio_vocabularies/translations/et_EE/LC_MESSAGES/messages.mo +0 -0
  127. invenio_vocabularies/translations/fa/LC_MESSAGES/messages.mo +0 -0
  128. invenio_vocabularies/translations/fa_IR/LC_MESSAGES/messages.mo +0 -0
  129. invenio_vocabularies/translations/fr/LC_MESSAGES/messages.mo +0 -0
  130. invenio_vocabularies/translations/fr_CI/LC_MESSAGES/messages.mo +0 -0
  131. invenio_vocabularies/translations/fr_FR/LC_MESSAGES/messages.mo +0 -0
  132. invenio_vocabularies/translations/gl/LC_MESSAGES/messages.mo +0 -0
  133. invenio_vocabularies/translations/hi_IN/LC_MESSAGES/messages.mo +0 -0
  134. invenio_vocabularies/translations/hr/LC_MESSAGES/messages.mo +0 -0
  135. invenio_vocabularies/translations/hu/LC_MESSAGES/messages.mo +0 -0
  136. invenio_vocabularies/translations/hu_HU/LC_MESSAGES/messages.mo +0 -0
  137. invenio_vocabularies/translations/it/LC_MESSAGES/messages.mo +0 -0
  138. invenio_vocabularies/translations/ja/LC_MESSAGES/messages.mo +0 -0
  139. invenio_vocabularies/translations/ka/LC_MESSAGES/messages.mo +0 -0
  140. invenio_vocabularies/translations/lt/LC_MESSAGES/messages.mo +0 -0
  141. invenio_vocabularies/translations/messages.pot +95 -48
  142. invenio_vocabularies/translations/ne/LC_MESSAGES/messages.mo +0 -0
  143. invenio_vocabularies/translations/no/LC_MESSAGES/messages.mo +0 -0
  144. invenio_vocabularies/translations/pl/LC_MESSAGES/messages.mo +0 -0
  145. invenio_vocabularies/translations/pt/LC_MESSAGES/messages.mo +0 -0
  146. invenio_vocabularies/translations/ro/LC_MESSAGES/messages.mo +0 -0
  147. invenio_vocabularies/translations/ru/LC_MESSAGES/messages.mo +0 -0
  148. invenio_vocabularies/translations/rw/LC_MESSAGES/messages.mo +0 -0
  149. invenio_vocabularies/translations/sk/LC_MESSAGES/messages.mo +0 -0
  150. invenio_vocabularies/translations/sv/LC_MESSAGES/messages.mo +0 -0
  151. invenio_vocabularies/translations/sv_SE/LC_MESSAGES/messages.mo +0 -0
  152. invenio_vocabularies/translations/tr/LC_MESSAGES/messages.mo +0 -0
  153. invenio_vocabularies/translations/uk/LC_MESSAGES/messages.mo +0 -0
  154. invenio_vocabularies/translations/uk_UA/LC_MESSAGES/messages.mo +0 -0
  155. invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.mo +0 -0
  156. invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.mo +0 -0
  157. invenio_vocabularies/views.py +12 -26
  158. invenio_vocabularies/webpack.py +3 -3
  159. {invenio_vocabularies-2.3.1.dist-info → invenio_vocabularies-6.3.1.dist-info}/METADATA +150 -6
  160. {invenio_vocabularies-2.3.1.dist-info → invenio_vocabularies-6.3.1.dist-info}/RECORD +165 -132
  161. {invenio_vocabularies-2.3.1.dist-info → invenio_vocabularies-6.3.1.dist-info}/WHEEL +1 -1
  162. {invenio_vocabularies-2.3.1.dist-info → invenio_vocabularies-6.3.1.dist-info}/entry_points.txt +17 -0
  163. {invenio_vocabularies-2.3.1.dist-info → invenio_vocabularies-6.3.1.dist-info}/AUTHORS.rst +0 -0
  164. {invenio_vocabularies-2.3.1.dist-info → invenio_vocabularies-6.3.1.dist-info}/LICENSE +0 -0
  165. {invenio_vocabularies-2.3.1.dist-info → invenio_vocabularies-6.3.1.dist-info}/top_level.txt +0 -0
@@ -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
  )
@@ -0,0 +1,179 @@
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 redistribute it and/or
7
+ # modify it under the terms of the MIT License; see LICENSE file for more
8
+ # details.
9
+
10
+ """Vocabulary services configs."""
11
+
12
+ import sqlalchemy as sa
13
+ from flask import current_app
14
+ from invenio_i18n import lazy_gettext as _
15
+ from invenio_records_resources.services import (
16
+ Link,
17
+ LinksTemplate,
18
+ RecordService,
19
+ RecordServiceConfig,
20
+ SearchOptions,
21
+ pagination_links,
22
+ )
23
+ from invenio_records_resources.services.base import (
24
+ ConditionalLink,
25
+ Service,
26
+ ServiceListResult,
27
+ )
28
+ from invenio_records_resources.services.records.components import DataComponent
29
+ from invenio_records_resources.services.records.params import (
30
+ FilterParam,
31
+ SuggestQueryParser,
32
+ )
33
+
34
+ from ..records.api import Vocabulary
35
+ from ..records.models import VocabularyType
36
+ from . import results
37
+ from .components import PIDComponent, VocabularyTypeComponent
38
+ from .permissions import PermissionPolicy
39
+ from .schema import TaskSchema, VocabularySchema
40
+
41
+
42
+ def is_custom_vocabulary_type(vocabulary_type, context):
43
+ """Check if the vocabulary type is a custom vocabulary type."""
44
+ return vocabulary_type["id"] in current_app.config.get(
45
+ "VOCABULARIES_CUSTOM_VOCABULARY_TYPES", []
46
+ )
47
+
48
+
49
+ class VocabularySearchOptions(SearchOptions):
50
+ """Search options for vocabularies."""
51
+
52
+ params_interpreters_cls = [
53
+ FilterParam.factory(param="tags", field="tags"),
54
+ ] + SearchOptions.params_interpreters_cls
55
+
56
+ suggest_parser_cls = SuggestQueryParser.factory(
57
+ fields=[
58
+ "id.text^100",
59
+ "id.text._2gram",
60
+ "id.text._3gram",
61
+ "title.en^5",
62
+ "title.en._2gram",
63
+ "title.en._3gram",
64
+ ],
65
+ )
66
+
67
+ sort_default = "bestmatch"
68
+
69
+ sort_default_no_query = "title"
70
+
71
+ sort_options = {
72
+ "bestmatch": dict(
73
+ title=_("Best match"),
74
+ fields=["_score"], # ES defaults to desc on `_score` field
75
+ ),
76
+ "title": dict(
77
+ title=_("Title"),
78
+ fields=["title_sort"],
79
+ ),
80
+ "newest": dict(
81
+ title=_("Newest"),
82
+ fields=["-created"],
83
+ ),
84
+ "oldest": dict(
85
+ title=_("Oldest"),
86
+ fields=["created"],
87
+ ),
88
+ }
89
+
90
+
91
+ class VocabularyTypeSearchOptions(SearchOptions):
92
+ """Search options for vocabulary types."""
93
+
94
+ sort_options = {
95
+ "id": dict(
96
+ title=_("ID"),
97
+ fields=["id"],
98
+ ),
99
+ }
100
+
101
+ sort_default = "id"
102
+
103
+ sort_default_no_query = "id"
104
+
105
+ sort_direction_options = {
106
+ "asc": dict(title=_("Ascending"), fn=sa.asc),
107
+ "desc": dict(title=_("Descending"), fn=sa.desc),
108
+ }
109
+
110
+ sort_direction_default = "asc"
111
+
112
+
113
+ class VocabulariesServiceConfig(RecordServiceConfig):
114
+ """Vocabulary service configuration."""
115
+
116
+ service_id = "vocabularies"
117
+ indexer_queue_name = "vocabularies"
118
+ permission_policy_cls = PermissionPolicy
119
+ record_cls = Vocabulary
120
+ schema = VocabularySchema
121
+ task_schema = TaskSchema
122
+
123
+ search = VocabularySearchOptions
124
+
125
+ components = [
126
+ # Order of components are important!
127
+ VocabularyTypeComponent,
128
+ DataComponent,
129
+ PIDComponent,
130
+ ]
131
+
132
+ links_item = {
133
+ "self": Link(
134
+ "{+api}/vocabularies/{type}/{id}",
135
+ vars=lambda record, vars: vars.update(
136
+ {
137
+ "id": record.pid.pid_value,
138
+ "type": record.type.id,
139
+ }
140
+ ),
141
+ ),
142
+ }
143
+
144
+ links_search = pagination_links("{+api}/vocabularies/{type}{?args*}")
145
+
146
+
147
+ class VocabularyTypesServiceConfig(RecordServiceConfig):
148
+ """Vocabulary types service configuration."""
149
+
150
+ service_id = "vocabulary_types"
151
+ permission_policy_cls = PermissionPolicy
152
+ record_cls = VocabularyType
153
+ schema = VocabularySchema # Works but should be VocabularyTypeSchema if this is defined at some point
154
+ result_list_cls = results.VocabularyTypeList
155
+
156
+ links_item = {
157
+ "self": ConditionalLink(
158
+ cond=is_custom_vocabulary_type,
159
+ if_=Link(
160
+ "{+api}/{id}",
161
+ vars=lambda vocab_type, vars: vars.update({"id": vocab_type["id"]}),
162
+ ),
163
+ else_=Link(
164
+ "{+api}/vocabularies/{id}",
165
+ vars=lambda vocab_type, vars: vars.update({"id": vocab_type["id"]}),
166
+ ),
167
+ )
168
+ }
169
+
170
+ search = VocabularyTypeSearchOptions
171
+
172
+ components = [
173
+ # Order of components are important!
174
+ VocabularyTypeComponent,
175
+ DataComponent,
176
+ PIDComponent,
177
+ ]
178
+
179
+ links_search = pagination_links("{+api}/vocabularies/{type}{?args*}")
@@ -1,12 +1,16 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2022 CERN.
3
+ # Copyright (C) 2022-2024 CERN.
4
4
  #
5
5
  # Invenio-RDM-Records is free software; you can redistribute it and/or modify
6
6
  # it under the terms of the MIT License; see LICENSE file for more details.
7
7
 
8
8
  """Custom Fields for InvenioRDM."""
9
9
 
10
+ from .subject import SUBJECT_FIELDS, SUBJECT_FIELDS_UI
10
11
  from .vocabulary import VocabularyCF
11
12
 
12
- __all__ = "VocabularyCF"
13
+ __all__ = [
14
+ "VocabularyCF",
15
+ "SUBJECT_FIELDS_UI" "SUBJECT_FIELDS",
16
+ ]
@@ -0,0 +1,82 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2024 CERN.
4
+ #
5
+ # Invenio-RDM-Records is free software; you can redistribute it and/or modify
6
+ # it under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+
9
+ """Custom fields."""
10
+
11
+ from invenio_i18n import lazy_gettext as _
12
+
13
+ from ...contrib.subjects.api import Subject
14
+ from ...contrib.subjects.schema import SubjectRelationSchema
15
+ from .vocabulary import VocabularyCF
16
+
17
+
18
+ class SubjectCF(VocabularyCF):
19
+ """Custom field for subjects."""
20
+
21
+ field_keys = ["id", "subject"]
22
+
23
+ def __init__(self, **kwargs):
24
+ """Constructor."""
25
+ super().__init__(
26
+ vocabulary_id="subjects",
27
+ schema=SubjectRelationSchema,
28
+ ui_schema=SubjectRelationSchema,
29
+ **kwargs,
30
+ )
31
+ self.pid_field = Subject.pid
32
+
33
+ @property
34
+ def mapping(self):
35
+ """Return the mapping."""
36
+ _mapping = {
37
+ "type": "object",
38
+ "properties": {
39
+ "@v": {"type": "keyword"},
40
+ "id": {"type": "keyword"},
41
+ "subject": {"type": "keyword"},
42
+ },
43
+ }
44
+
45
+ return _mapping
46
+
47
+
48
+ SUBJECT_FIELDS_UI = [
49
+ {
50
+ "section": _("Subjects"),
51
+ "fields": [
52
+ dict(
53
+ field="subjects",
54
+ ui_widget="SubjectAutocompleteDropdown",
55
+ isGenericVocabulary=False,
56
+ props=dict(
57
+ label="Keywords and subjects",
58
+ icon="tag",
59
+ description="The subjects related to the community",
60
+ placeholder="Search for a subject by name e.g. Psychology ...",
61
+ autocompleteFrom="api/subjects",
62
+ noQueryMessage="Search for subjects...",
63
+ autocompleteFromAcceptHeader="application/vnd.inveniordm.v1+json",
64
+ required=False,
65
+ multiple=True,
66
+ clearable=True,
67
+ allowAdditions=False,
68
+ ),
69
+ template="invenio_vocabularies/subjects.html",
70
+ )
71
+ ],
72
+ }
73
+ ]
74
+
75
+
76
+ SUBJECT_FIELDS = {
77
+ SubjectCF(
78
+ name="subjects",
79
+ multiple=True,
80
+ dump_options=False,
81
+ )
82
+ }
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2022 CERN.
3
+ # Copyright (C) 2022-2024 CERN.
4
4
  #
5
5
  # Invenio-RDM-Records is free software; you can redistribute it and/or modify
6
6
  # it under the terms of the MIT License; see LICENSE file for more details.
@@ -12,6 +12,7 @@ from invenio_records_resources.services.custom_fields.base import BaseCF
12
12
  from marshmallow import fields
13
13
 
14
14
  from ...proxies import current_service
15
+ from ...records.api import Vocabulary
15
16
  from ...resources.serializer import VocabularyL10NItemSchema
16
17
  from ...services.schema import VocabularyRelationSchema
17
18
 
@@ -38,7 +39,7 @@ class VocabularyCF(BaseCF):
38
39
  sort_by=None,
39
40
  schema=VocabularyRelationSchema,
40
41
  ui_schema=VocabularyL10NItemSchema,
41
- **kwargs
42
+ **kwargs,
42
43
  ):
43
44
  """Constructor."""
44
45
  super().__init__(name, **kwargs)
@@ -49,6 +50,7 @@ class VocabularyCF(BaseCF):
49
50
  self.sort_by = sort_by
50
51
  self.schema = schema
51
52
  self.ui_schema = ui_schema
53
+ self.pid_field = Vocabulary.pid.with_type_ctx(self.vocabulary_id)
52
54
 
53
55
  @property
54
56
  def mapping(self):
@@ -58,7 +60,7 @@ class VocabularyCF(BaseCF):
58
60
  "properties": {
59
61
  "@v": {"type": "keyword"},
60
62
  "id": {"type": "keyword"},
61
- "title": {"type": "object", "dynamic": True},
63
+ "title": {"type": "object", "dynamic": "true"},
62
64
  },
63
65
  }
64
66
 
@@ -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
@@ -21,3 +21,5 @@ class PermissionPolicy(RecordPermissionPolicy):
21
21
  can_update = [SystemProcess()]
22
22
  can_delete = [SystemProcess()]
23
23
  can_manage = [SystemProcess()]
24
+ # this permission is needed for the /api/vocabularies/ endpoint
25
+ can_list_vocabularies = [SystemProcess(), AnyUser()]
@@ -0,0 +1,110 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2024 CERN.
4
+ # Copyright (C) 2024 Uni Münster.
5
+ #
6
+ # Invenio-Vocabularies is free software; you can redistribute it and/or
7
+ # modify it under the terms of the MIT License; see LICENSE file for more
8
+ # details.
9
+
10
+ """Vocabulary results."""
11
+
12
+ from flask import current_app
13
+ from invenio_records_resources.proxies import current_service_registry
14
+ from invenio_records_resources.services import RecordServiceConfig
15
+ from invenio_records_resources.services.records.results import RecordList
16
+ from invenio_search import current_search_client
17
+
18
+ from ..proxies import current_service
19
+
20
+
21
+ class VocabularyTypeList(RecordList):
22
+ """Ensures that vocabulary type metadata is returned in the proper format."""
23
+
24
+ @property
25
+ def total(self):
26
+ """Get total number of hits."""
27
+ return self._results.total
28
+
29
+ @property
30
+ def custom_vocabulary_names(self):
31
+ """Checks whether vocabulary is a custom vocabulary."""
32
+ return current_app.config.get("VOCABULARIES_CUSTOM_VOCABULARY_TYPES", [])
33
+
34
+ def to_dict(self):
35
+ """Formats result to a dict of hits."""
36
+ config_vocab_types = current_app.config.get(
37
+ "INVENIO_VOCABULARY_TYPE_METADATA", {}
38
+ )
39
+
40
+ count_terms_agg = {}
41
+ generic_stats = self._generic_vocabulary_statistics()
42
+ custom_stats = self._custom_vocabulary_statistics()
43
+
44
+ for k in generic_stats.keys() | custom_stats.keys():
45
+ count_terms_agg[k] = generic_stats.get(k, 0) + custom_stats.get(k, 0)
46
+
47
+ hits = self._results.items
48
+
49
+ # Extend database data with configuration & aggregation data.
50
+ results = []
51
+ for db_vocab_type in hits:
52
+ result = {
53
+ "id": db_vocab_type.id,
54
+ "pid_type": db_vocab_type.pid_type,
55
+ "count": count_terms_agg.get(db_vocab_type.id, 0),
56
+ "is_custom_vocabulary": db_vocab_type.id
57
+ in self.custom_vocabulary_names,
58
+ }
59
+
60
+ if db_vocab_type.id in config_vocab_types:
61
+ for k, v in config_vocab_types[db_vocab_type.id].items():
62
+ result[k] = v
63
+
64
+ results.append(result)
65
+
66
+ for hit in results:
67
+ if self._links_item_tpl:
68
+ hit["links"] = self._links_item_tpl.expand(self._identity, hit)
69
+
70
+ res = {
71
+ "hits": {
72
+ "hits": results,
73
+ "total": self.total,
74
+ }
75
+ }
76
+
77
+ if self._params:
78
+ if self._links_tpl:
79
+ res["links"] = self._links_tpl.expand(self._identity, self.pagination)
80
+
81
+ return res
82
+
83
+ def _custom_vocabulary_statistics(self):
84
+ # query database for count of terms in custom vocabularies
85
+ returndict = {}
86
+ for vocab_type in self.custom_vocabulary_names:
87
+ custom_service = current_service_registry.get(vocab_type)
88
+ record_cls = custom_service.config.record_cls
89
+ returndict[vocab_type] = record_cls.model_cls.query.count()
90
+
91
+ return returndict
92
+
93
+ def _generic_vocabulary_statistics(self):
94
+ # Opensearch query for generic vocabularies
95
+ config: RecordServiceConfig = (
96
+ current_service.config
97
+ ) # TODO: Where to get the config from here? current_service is None
98
+ search_opts = config.search
99
+
100
+ search = search_opts.search_cls(
101
+ using=current_search_client,
102
+ index=config.record_cls.index.search_alias,
103
+ )
104
+
105
+ search.aggs.bucket("vocabularies", {"terms": {"field": "type.id", "size": 100}})
106
+
107
+ search_result = search.execute()
108
+ buckets = search_result.aggs.to_dict()["vocabularies"]["buckets"]
109
+
110
+ return {bucket["key"]: bucket["doc_count"] for bucket in buckets}