invenio-vocabularies 1.2.0__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 (239) 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/alembic/4a9a4fd235f8_create_vocabulary_schemes.py +4 -4
  6. invenio_vocabularies/alembic/4f365fced43f_create_vocabularies_tables.py +2 -2
  7. invenio_vocabularies/alembic/55a700f897b6_add_names_and_afiliations_pid_column.py +96 -0
  8. invenio_vocabularies/alembic/676dd587542d_create_funders_vocabulary_table.py +1 -1
  9. invenio_vocabularies/alembic/e1146238edd3_create_awards_table.py +1 -1
  10. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/.eslintrc.yml +11 -0
  11. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/.prettierrc +1 -0
  12. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/index.js +7 -0
  13. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/package.json +25 -0
  14. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/AwardResults.js +95 -0
  15. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js +139 -0
  16. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FunderDropdown.js +87 -0
  17. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.js +223 -0
  18. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.test.js +1 -0
  19. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingFieldItem.js +152 -0
  20. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingModal.js +270 -0
  21. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/NoAwardResults.js +37 -0
  22. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/index.js +8 -0
  23. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/index.js +7 -0
  24. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/index.js +7 -0
  25. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/index.js +7 -0
  26. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/i18next-scanner.config.js +63 -0
  27. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/i18next.js +36 -0
  28. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/index.js +1 -0
  29. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/package.json +53 -0
  30. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/scripts/compileCatalog.js +39 -0
  31. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/scripts/initCatalog.js +19 -0
  32. invenio_vocabularies/cli.py +31 -44
  33. invenio_vocabularies/config.py +74 -7
  34. invenio_vocabularies/contrib/affiliations/affiliations.py +22 -6
  35. invenio_vocabularies/contrib/affiliations/api.py +1 -2
  36. invenio_vocabularies/contrib/affiliations/config.py +10 -5
  37. invenio_vocabularies/contrib/affiliations/datastreams.py +186 -0
  38. invenio_vocabularies/contrib/affiliations/facets.py +36 -0
  39. invenio_vocabularies/contrib/affiliations/jsonschemas/affiliations/affiliation-v1.0.0.json +38 -7
  40. invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v1.0.0.json +22 -1
  41. invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v2.0.0.json +171 -0
  42. invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v1.0.0.json +22 -1
  43. invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v2.0.0.json +171 -0
  44. invenio_vocabularies/contrib/affiliations/mappings/v7/affiliations/affiliation-v1.0.0.json +22 -1
  45. invenio_vocabularies/contrib/affiliations/schema.py +23 -5
  46. invenio_vocabularies/contrib/affiliations/services.py +1 -2
  47. invenio_vocabularies/contrib/awards/awards.py +18 -6
  48. invenio_vocabularies/contrib/awards/config.py +1 -3
  49. invenio_vocabularies/contrib/awards/datastreams.py +246 -3
  50. invenio_vocabularies/contrib/awards/jsonschemas/awards/award-v1.0.0.json +41 -0
  51. invenio_vocabularies/contrib/awards/mappings/os-v1/awards/award-v1.0.0.json +53 -1
  52. invenio_vocabularies/contrib/awards/mappings/os-v2/awards/award-v1.0.0.json +53 -1
  53. invenio_vocabularies/contrib/awards/mappings/v7/awards/award-v1.0.0.json +53 -1
  54. invenio_vocabularies/contrib/awards/schema.py +27 -35
  55. invenio_vocabularies/contrib/awards/serializer.py +9 -1
  56. invenio_vocabularies/contrib/awards/services.py +1 -2
  57. invenio_vocabularies/contrib/common/__init__.py +9 -0
  58. invenio_vocabularies/contrib/common/openaire/__init__.py +9 -0
  59. invenio_vocabularies/contrib/common/openaire/datastreams.py +84 -0
  60. invenio_vocabularies/contrib/common/ror/__init__.py +9 -0
  61. invenio_vocabularies/contrib/common/ror/datastreams.py +220 -0
  62. invenio_vocabularies/contrib/funders/config.py +12 -5
  63. invenio_vocabularies/contrib/funders/datastreams.py +40 -62
  64. invenio_vocabularies/contrib/funders/facets.py +13 -5
  65. invenio_vocabularies/contrib/funders/funders.py +4 -2
  66. invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json +36 -1
  67. invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json +22 -1
  68. invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v2.0.0.json +156 -0
  69. invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json +22 -1
  70. invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v2.0.0.json +156 -0
  71. invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json +22 -1
  72. invenio_vocabularies/contrib/funders/schema.py +8 -0
  73. invenio_vocabularies/contrib/funders/serializer.py +2 -1
  74. invenio_vocabularies/contrib/names/config.py +5 -3
  75. invenio_vocabularies/contrib/names/datastreams.py +177 -38
  76. invenio_vocabularies/contrib/names/jsonschemas/names/name-v1.0.0.json +2 -6
  77. invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v1.0.0.json +3 -0
  78. invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v2.0.0.json +150 -0
  79. invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v1.0.0.json +3 -0
  80. invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v2.0.0.json +150 -0
  81. invenio_vocabularies/contrib/names/mappings/v7/names/name-v1.0.0.json +3 -0
  82. invenio_vocabularies/contrib/names/names.py +29 -13
  83. invenio_vocabularies/contrib/names/permissions.py +20 -0
  84. invenio_vocabularies/contrib/names/s3client.py +44 -0
  85. invenio_vocabularies/contrib/names/schema.py +31 -4
  86. invenio_vocabularies/contrib/subjects/config.py +9 -3
  87. invenio_vocabularies/contrib/subjects/datastreams.py +61 -0
  88. invenio_vocabularies/contrib/subjects/euroscivoc/__init__.py +9 -0
  89. invenio_vocabularies/contrib/subjects/euroscivoc/datastreams.py +171 -0
  90. invenio_vocabularies/contrib/subjects/jsonschemas/subjects/subject-v1.0.0.json +31 -0
  91. invenio_vocabularies/contrib/subjects/mappings/os-v1/subjects/subject-v1.0.0.json +35 -0
  92. invenio_vocabularies/contrib/subjects/mappings/os-v2/subjects/subject-v1.0.0.json +35 -0
  93. invenio_vocabularies/contrib/subjects/mappings/v7/subjects/subject-v1.0.0.json +35 -0
  94. invenio_vocabularies/contrib/subjects/mesh/__init__.py +9 -0
  95. invenio_vocabularies/contrib/subjects/mesh/datastreams.py +43 -0
  96. invenio_vocabularies/contrib/subjects/schema.py +47 -5
  97. invenio_vocabularies/contrib/subjects/subjects.py +10 -0
  98. invenio_vocabularies/datastreams/datastreams.py +61 -13
  99. invenio_vocabularies/datastreams/factories.py +1 -2
  100. invenio_vocabularies/datastreams/readers.py +138 -29
  101. invenio_vocabularies/datastreams/tasks.py +37 -0
  102. invenio_vocabularies/datastreams/transformers.py +17 -27
  103. invenio_vocabularies/datastreams/writers.py +116 -14
  104. invenio_vocabularies/datastreams/xml.py +34 -0
  105. invenio_vocabularies/ext.py +59 -5
  106. invenio_vocabularies/factories.py +137 -0
  107. invenio_vocabularies/jobs.py +133 -0
  108. invenio_vocabularies/proxies.py +2 -2
  109. invenio_vocabularies/records/jsonschemas/vocabularies/definitions-v1.0.0.json +7 -0
  110. invenio_vocabularies/records/jsonschemas/vocabularies/vocabulary-v1.0.0.json +1 -4
  111. invenio_vocabularies/records/mappings/os-v1/vocabularies/vocabulary-v1.0.0.json +3 -3
  112. invenio_vocabularies/records/mappings/os-v2/vocabularies/vocabulary-v1.0.0.json +3 -3
  113. invenio_vocabularies/records/mappings/v7/vocabularies/vocabulary-v1.0.0.json +3 -3
  114. invenio_vocabularies/records/models.py +8 -10
  115. invenio_vocabularies/records/pidprovider.py +1 -2
  116. invenio_vocabularies/records/systemfields/relations.py +2 -2
  117. invenio_vocabularies/resources/__init__.py +9 -1
  118. invenio_vocabularies/resources/config.py +105 -0
  119. invenio_vocabularies/resources/resource.py +31 -41
  120. invenio_vocabularies/resources/schema.py +2 -1
  121. invenio_vocabularies/services/__init__.py +5 -2
  122. invenio_vocabularies/services/config.py +179 -0
  123. invenio_vocabularies/services/custom_fields/__init__.py +6 -2
  124. invenio_vocabularies/services/custom_fields/subject.py +82 -0
  125. invenio_vocabularies/services/custom_fields/vocabulary.py +19 -9
  126. invenio_vocabularies/services/facets.py +67 -37
  127. invenio_vocabularies/services/permissions.py +3 -1
  128. invenio_vocabularies/services/results.py +110 -0
  129. invenio_vocabularies/services/schema.py +39 -2
  130. invenio_vocabularies/services/service.py +46 -94
  131. invenio_vocabularies/services/tasks.py +1 -1
  132. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/subjects.html +23 -0
  133. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabularies-list.html +12 -0
  134. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabulary-details.html +71 -0
  135. invenio_vocabularies/translations/af/LC_MESSAGES/messages.mo +0 -0
  136. invenio_vocabularies/translations/af/LC_MESSAGES/messages.po +1 -1
  137. invenio_vocabularies/translations/ar/LC_MESSAGES/messages.mo +0 -0
  138. invenio_vocabularies/translations/ar/LC_MESSAGES/messages.po +9 -8
  139. invenio_vocabularies/translations/bg/LC_MESSAGES/messages.mo +0 -0
  140. invenio_vocabularies/translations/bg/LC_MESSAGES/messages.po +1 -1
  141. invenio_vocabularies/translations/ca/LC_MESSAGES/messages.mo +0 -0
  142. invenio_vocabularies/translations/ca/LC_MESSAGES/messages.po +1 -1
  143. invenio_vocabularies/translations/cs/LC_MESSAGES/messages.mo +0 -0
  144. invenio_vocabularies/translations/cs/LC_MESSAGES/messages.po +1 -1
  145. invenio_vocabularies/translations/da/LC_MESSAGES/messages.mo +0 -0
  146. invenio_vocabularies/translations/da/LC_MESSAGES/messages.po +1 -1
  147. invenio_vocabularies/translations/de/LC_MESSAGES/messages.mo +0 -0
  148. invenio_vocabularies/translations/de/LC_MESSAGES/messages.po +1 -1
  149. invenio_vocabularies/translations/de_AT/LC_MESSAGES/messages.mo +0 -0
  150. invenio_vocabularies/translations/de_AT/LC_MESSAGES/messages.po +139 -0
  151. invenio_vocabularies/translations/de_DE/LC_MESSAGES/messages.mo +0 -0
  152. invenio_vocabularies/translations/de_DE/LC_MESSAGES/messages.po +139 -0
  153. invenio_vocabularies/translations/el/LC_MESSAGES/messages.mo +0 -0
  154. invenio_vocabularies/translations/el/LC_MESSAGES/messages.po +1 -1
  155. invenio_vocabularies/translations/en/LC_MESSAGES/messages.mo +0 -0
  156. invenio_vocabularies/translations/en_AT/LC_MESSAGES/messages.mo +0 -0
  157. invenio_vocabularies/translations/en_AT/LC_MESSAGES/messages.po +139 -0
  158. invenio_vocabularies/translations/en_HU/LC_MESSAGES/messages.mo +0 -0
  159. invenio_vocabularies/translations/en_HU/LC_MESSAGES/messages.po +139 -0
  160. invenio_vocabularies/translations/es/LC_MESSAGES/messages.mo +0 -0
  161. invenio_vocabularies/translations/es/LC_MESSAGES/messages.po +1 -1
  162. invenio_vocabularies/translations/es_CU/LC_MESSAGES/messages.mo +0 -0
  163. invenio_vocabularies/translations/es_CU/LC_MESSAGES/messages.po +139 -0
  164. invenio_vocabularies/translations/es_MX/LC_MESSAGES/messages.mo +0 -0
  165. invenio_vocabularies/translations/es_MX/LC_MESSAGES/messages.po +139 -0
  166. invenio_vocabularies/translations/et/LC_MESSAGES/messages.mo +0 -0
  167. invenio_vocabularies/translations/et/LC_MESSAGES/messages.po +1 -1
  168. invenio_vocabularies/translations/et_EE/LC_MESSAGES/messages.mo +0 -0
  169. invenio_vocabularies/translations/et_EE/LC_MESSAGES/messages.po +1 -1
  170. invenio_vocabularies/translations/fa/LC_MESSAGES/messages.mo +0 -0
  171. invenio_vocabularies/translations/fa/LC_MESSAGES/messages.po +1 -1
  172. invenio_vocabularies/translations/fa_IR/LC_MESSAGES/messages.mo +0 -0
  173. invenio_vocabularies/translations/fa_IR/LC_MESSAGES/messages.po +139 -0
  174. invenio_vocabularies/translations/fr/LC_MESSAGES/messages.mo +0 -0
  175. invenio_vocabularies/translations/fr/LC_MESSAGES/messages.po +1 -1
  176. invenio_vocabularies/translations/fr_CI/LC_MESSAGES/messages.mo +0 -0
  177. invenio_vocabularies/translations/fr_CI/LC_MESSAGES/messages.po +139 -0
  178. invenio_vocabularies/translations/fr_FR/LC_MESSAGES/messages.mo +0 -0
  179. invenio_vocabularies/translations/fr_FR/LC_MESSAGES/messages.po +139 -0
  180. invenio_vocabularies/translations/gl/LC_MESSAGES/messages.mo +0 -0
  181. invenio_vocabularies/translations/gl/LC_MESSAGES/messages.po +1 -1
  182. invenio_vocabularies/translations/hi_IN/LC_MESSAGES/messages.mo +0 -0
  183. invenio_vocabularies/translations/hi_IN/LC_MESSAGES/messages.po +139 -0
  184. invenio_vocabularies/translations/hr/LC_MESSAGES/messages.mo +0 -0
  185. invenio_vocabularies/translations/hr/LC_MESSAGES/messages.po +1 -1
  186. invenio_vocabularies/translations/hu/LC_MESSAGES/messages.mo +0 -0
  187. invenio_vocabularies/translations/hu/LC_MESSAGES/messages.po +4 -4
  188. invenio_vocabularies/translations/hu_HU/LC_MESSAGES/messages.mo +0 -0
  189. invenio_vocabularies/translations/hu_HU/LC_MESSAGES/messages.po +139 -0
  190. invenio_vocabularies/translations/it/LC_MESSAGES/messages.mo +0 -0
  191. invenio_vocabularies/translations/it/LC_MESSAGES/messages.po +4 -3
  192. invenio_vocabularies/translations/ja/LC_MESSAGES/messages.mo +0 -0
  193. invenio_vocabularies/translations/ja/LC_MESSAGES/messages.po +1 -1
  194. invenio_vocabularies/translations/ka/LC_MESSAGES/messages.mo +0 -0
  195. invenio_vocabularies/translations/ka/LC_MESSAGES/messages.po +1 -1
  196. invenio_vocabularies/translations/lt/LC_MESSAGES/messages.mo +0 -0
  197. invenio_vocabularies/translations/lt/LC_MESSAGES/messages.po +1 -1
  198. invenio_vocabularies/translations/messages.pot +95 -48
  199. invenio_vocabularies/translations/ne/LC_MESSAGES/messages.mo +0 -0
  200. invenio_vocabularies/translations/ne/LC_MESSAGES/messages.po +139 -0
  201. invenio_vocabularies/translations/no/LC_MESSAGES/messages.mo +0 -0
  202. invenio_vocabularies/translations/no/LC_MESSAGES/messages.po +1 -1
  203. invenio_vocabularies/translations/pl/LC_MESSAGES/messages.mo +0 -0
  204. invenio_vocabularies/translations/pl/LC_MESSAGES/messages.po +1 -1
  205. invenio_vocabularies/translations/pt/LC_MESSAGES/messages.mo +0 -0
  206. invenio_vocabularies/translations/pt/LC_MESSAGES/messages.po +1 -1
  207. invenio_vocabularies/translations/ro/LC_MESSAGES/messages.mo +0 -0
  208. invenio_vocabularies/translations/ro/LC_MESSAGES/messages.po +1 -1
  209. invenio_vocabularies/translations/ru/LC_MESSAGES/messages.mo +0 -0
  210. invenio_vocabularies/translations/ru/LC_MESSAGES/messages.po +1 -1
  211. invenio_vocabularies/translations/rw/LC_MESSAGES/messages.mo +0 -0
  212. invenio_vocabularies/translations/rw/LC_MESSAGES/messages.po +1 -1
  213. invenio_vocabularies/translations/sk/LC_MESSAGES/messages.mo +0 -0
  214. invenio_vocabularies/translations/sk/LC_MESSAGES/messages.po +1 -1
  215. invenio_vocabularies/translations/sv/LC_MESSAGES/messages.mo +0 -0
  216. invenio_vocabularies/translations/sv/LC_MESSAGES/messages.po +4 -3
  217. invenio_vocabularies/translations/sv_SE/LC_MESSAGES/messages.mo +0 -0
  218. invenio_vocabularies/translations/sv_SE/LC_MESSAGES/messages.po +139 -0
  219. invenio_vocabularies/translations/tr/LC_MESSAGES/messages.mo +0 -0
  220. invenio_vocabularies/translations/tr/LC_MESSAGES/messages.po +1 -1
  221. invenio_vocabularies/translations/uk/LC_MESSAGES/messages.mo +0 -0
  222. invenio_vocabularies/translations/uk/LC_MESSAGES/messages.po +17 -13
  223. invenio_vocabularies/translations/uk_UA/LC_MESSAGES/messages.mo +0 -0
  224. invenio_vocabularies/translations/uk_UA/LC_MESSAGES/messages.po +139 -0
  225. invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.mo +0 -0
  226. invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.po +1 -1
  227. invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.mo +0 -0
  228. invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.po +1 -1
  229. invenio_vocabularies/views.py +12 -26
  230. invenio_vocabularies/webpack.py +51 -0
  231. invenio_vocabularies-6.3.1.dist-info/METADATA +346 -0
  232. invenio_vocabularies-6.3.1.dist-info/RECORD +306 -0
  233. {invenio_vocabularies-1.2.0.dist-info → invenio_vocabularies-6.3.1.dist-info}/WHEEL +1 -1
  234. {invenio_vocabularies-1.2.0.dist-info → invenio_vocabularies-6.3.1.dist-info}/entry_points.txt +20 -0
  235. invenio_vocabularies-1.2.0.dist-info/METADATA +0 -133
  236. invenio_vocabularies-1.2.0.dist-info/RECORD +0 -220
  237. {invenio_vocabularies-1.2.0.dist-info → invenio_vocabularies-6.3.1.dist-info}/AUTHORS.rst +0 -0
  238. {invenio_vocabularies-1.2.0.dist-info → invenio_vocabularies-6.3.1.dist-info}/LICENSE +0 -0
  239. {invenio_vocabularies-1.2.0.dist-info → invenio_vocabularies-6.3.1.dist-info}/top_level.txt +0 -0
@@ -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
 
@@ -30,7 +31,15 @@ class VocabularyCF(BaseCF):
30
31
  """
31
32
 
32
33
  def __init__(
33
- self, name, vocabulary_id, multiple=False, dump_options=True, **kwargs
34
+ self,
35
+ name,
36
+ vocabulary_id,
37
+ multiple=False,
38
+ dump_options=True,
39
+ sort_by=None,
40
+ schema=VocabularyRelationSchema,
41
+ ui_schema=VocabularyL10NItemSchema,
42
+ **kwargs,
34
43
  ):
35
44
  """Constructor."""
36
45
  super().__init__(name, **kwargs)
@@ -38,6 +47,10 @@ class VocabularyCF(BaseCF):
38
47
  self.vocabulary_id = vocabulary_id
39
48
  self.dump_options = dump_options
40
49
  self.multiple = multiple
50
+ self.sort_by = sort_by
51
+ self.schema = schema
52
+ self.ui_schema = ui_schema
53
+ self.pid_field = Vocabulary.pid.with_type_ctx(self.vocabulary_id)
41
54
 
42
55
  @property
43
56
  def mapping(self):
@@ -47,7 +60,7 @@ class VocabularyCF(BaseCF):
47
60
  "properties": {
48
61
  "@v": {"type": "keyword"},
49
62
  "id": {"type": "keyword"},
50
- "title": {"type": "object", "dynamic": True},
63
+ "title": {"type": "object", "dynamic": "true"},
51
64
  },
52
65
  }
53
66
 
@@ -56,9 +69,7 @@ class VocabularyCF(BaseCF):
56
69
  @property
57
70
  def field(self):
58
71
  """Marshmallow schema for vocabulary custom fields."""
59
- return fields.Nested(
60
- VocabularyRelationSchema, many=self.multiple, **self._field_args
61
- )
72
+ return fields.Nested(self.schema, many=self.multiple, **self._field_args)
62
73
 
63
74
  @property
64
75
  def ui_field(self):
@@ -67,9 +78,7 @@ class VocabularyCF(BaseCF):
67
78
  This schema is used in the UIJSONSerializer and controls how the field will be
68
79
  dumped in the UI. It takes responsibility of the localization of strings.
69
80
  """
70
- return fields.Nested(
71
- VocabularyL10NItemSchema, many=self.multiple, **self._field_args
72
- )
81
+ return fields.Nested(self.ui_schema, many=self.multiple, **self._field_args)
73
82
 
74
83
  def options(self, identity):
75
84
  """Return UI serialized vocabulary items."""
@@ -78,6 +87,7 @@ class VocabularyCF(BaseCF):
78
87
  identity,
79
88
  fields=self.field_keys,
80
89
  type=self.vocabulary_id,
90
+ sort=self.sort_by,
81
91
  )
82
92
  options = []
83
93
  for vocab in vocabs:
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2021-2022 CERN.
3
+ # Copyright (C) 2021-2023 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
@@ -9,6 +9,7 @@
9
9
  """Vocabulary facets."""
10
10
 
11
11
  from flask_principal import AnonymousIdentity
12
+ from invenio_cache.decorators import cached_with_expiration
12
13
  from invenio_i18n.ext import current_i18n
13
14
  from invenio_records_resources.proxies import current_service_registry
14
15
  from invenio_records_resources.services.errors import FacetNotFoundError
@@ -19,6 +20,31 @@ from sqlalchemy.exc import NoResultFound
19
20
  from ..proxies import current_service
20
21
 
21
22
 
23
+ def get_service(service_id):
24
+ """Get the service object by name.
25
+
26
+ It is required to access the registry lazily, to avoid "out of
27
+ application context" errors.
28
+ """
29
+ return current_service_registry.get(service_id) if service_id else current_service
30
+
31
+
32
+ def get_vocabs(service_id, type, fields, ids):
33
+ """Fetch vocabulary values by ids, using the service."""
34
+ service = get_service(service_id)
35
+ vocabs = service.read_many(
36
+ AnonymousIdentity(), type=type, ids=list(ids), fields=list(fields)
37
+ )
38
+ return list(vocabs.hits) # the service returns a generator
39
+
40
+
41
+ @cached_with_expiration
42
+ def get_cached_vocab(service_id, type, fields, id_):
43
+ """Cache vocabulary values by type in-memory."""
44
+ vocabs = get_vocabs(service_id, type, fields, [id_])
45
+ return vocabs[0] if vocabs else None
46
+
47
+
22
48
  def lazy_get_label(vocab_item):
23
49
  """Lazy evaluation of a localized vocabulary label."""
24
50
  params = {"locale": current_i18n.locale, "default_locale": "en"}
@@ -29,56 +55,60 @@ def lazy_get_label(vocab_item):
29
55
  class VocabularyLabels:
30
56
  """Fetching of vocabulary labels for facets."""
31
57
 
32
- def __init__(self, vocabulary, cache=False, service_name=None, id_field="id"):
33
- """Initialize the labels."""
58
+ def __init__(
59
+ self, vocabulary, cache=True, cache_ttl=3600, service_id=None, id_field="id"
60
+ ):
61
+ """Initialize the labels.
62
+
63
+ :param vocabulary: the name of the vocabulary type.
64
+ :param cache: use simple process in-memory cache when True.
65
+ :param cache_ttl: cache expiration in seconds.
66
+ :param service_id: the id of the registered service to be used
67
+ when fetching values for the vocabulary.
68
+ :param id_field: the name of the `id` field.
69
+ """
34
70
  self.vocabulary = vocabulary
35
71
  self.cache = cache
36
- self.fields = ["id", "title"] # not configurable
37
- self.service_name = service_name
72
+ self.cache_ttl = cache_ttl
73
+ self.fields = ("id", "title") # not configurable
74
+ self.service_id = service_id
38
75
  self.id_field = id_field
39
76
 
40
- @property
41
- def service(self):
42
- """Service property.
43
-
44
- It is required to access the regitry lazily to avoid out of
45
- application context errors.
46
- """
47
- if not self.service_name:
48
- return current_service
49
- return current_service_registry.get(self.service_name)
50
-
51
77
  def _vocab_to_label(self, vocab):
52
78
  """Returns the label string for a vocabulary entry."""
53
79
  return lazy_get_label(vocab["title"])
54
80
 
55
81
  def __call__(self, ids):
56
82
  """Return the mapping when evaluated."""
57
- identity = AnonymousIdentity()
83
+ if not ids:
84
+ return {}
85
+
86
+ labels = {}
58
87
  try:
59
- if not self.cache:
60
- vocabs = self.service.read_many(
61
- identity, type=self.vocabulary, ids=ids, fields=self.fields
62
- )
88
+ if self.cache:
89
+ for id_ in ids:
90
+ vocab = get_cached_vocab(
91
+ self.service_id,
92
+ self.vocabulary,
93
+ self.fields,
94
+ id_,
95
+ cache_ttl=self.cache_ttl,
96
+ )
97
+ if not vocab:
98
+ continue
99
+ labels[vocab[self.id_field]] = self._vocab_to_label(vocab)
63
100
  else:
64
- vocabs = self.service.read_all(
65
- identity, type=self.vocabulary, fields=self.fields
101
+ vocab_list = get_vocabs(
102
+ self.service_id,
103
+ self.vocabulary,
104
+ self.fields,
105
+ ids,
66
106
  )
107
+ for vocab in vocab_list:
108
+ id_ = vocab[self.id_field]
109
+ if id_ in ids:
110
+ labels[id_] = self._vocab_to_label(vocab)
67
111
  except NoResultFound:
68
112
  raise FacetNotFoundError(self.vocabulary)
69
113
 
70
- labels = {}
71
- vocab_list = list(vocabs.hits) # the service returns a generator
72
- ids = set(ids)
73
- seen = set()
74
- for vocab in vocab_list:
75
- # cannot loop over ids because vocab id is inside each item
76
- if len(ids) == len(seen):
77
- break
78
-
79
- id_ = vocab[self.id_field]
80
- if id_ in ids:
81
- labels[id_] = self._vocab_to_label(vocab)
82
- seen.add(id_)
83
-
84
114
  return labels
@@ -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}
@@ -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
  #
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
@@ -14,6 +14,8 @@ from marshmallow import (
14
14
  Schema,
15
15
  ValidationError,
16
16
  fields,
17
+ post_load,
18
+ pre_dump,
17
19
  pre_load,
18
20
  validate,
19
21
  validates_schema,
@@ -33,6 +35,9 @@ class BaseVocabularyRelationSchema(Schema):
33
35
 
34
36
  id = SanitizedUnicode(required=True)
35
37
 
38
+ # Nested field type for administration UI form generation
39
+ administration_schema_type = "vocabulary"
40
+
36
41
 
37
42
  class VocabularyRelationSchema(BaseVocabularyRelationSchema):
38
43
  """Vocabulary relation schema."""
@@ -61,6 +66,9 @@ class ContribVocabularyRelationSchema(Schema):
61
66
  ftf_name = None # free text field name
62
67
  parent_field_name = None
63
68
 
69
+ # Nested field type for administration UI form generation
70
+ administration_schema_type = "vocabulary"
71
+
64
72
  @validates_schema
65
73
  def validate_relation_schema(self, data, **kwargs):
66
74
  """Validates that either id either the free text field are present."""
@@ -88,16 +96,45 @@ class BaseVocabularySchema(BaseRecordSchema):
88
96
  title = i18n_strings
89
97
  description = i18n_strings
90
98
  icon = fields.Str(allow_none=False)
99
+ tags = fields.List(SanitizedUnicode())
100
+
101
+ # Nested field type for administration UI form generation
102
+ administration_schema_type = "vocabulary"
91
103
 
92
104
 
93
105
  class VocabularySchema(BaseVocabularySchema):
94
106
  """Service schema for vocabulary records."""
95
107
 
96
108
  props = fields.Dict(allow_none=False, keys=fields.Str(), values=fields.Str())
97
- tags = fields.List(SanitizedUnicode())
98
109
  type = fields.Str(attribute="type.id", required=True)
99
110
 
100
111
 
112
+ class ModePIDFieldVocabularyMixin:
113
+ """Mixin for vocabularies using a model field for their PID."""
114
+
115
+ @validates_schema
116
+ def validate_id(self, data, **kwargs):
117
+ """Validates ID."""
118
+ is_create = "record" not in self.context
119
+ if is_create and "id" not in data:
120
+ raise ValidationError(_("Missing PID."), "id")
121
+ if not is_create:
122
+ data.pop("id", None)
123
+
124
+ @post_load(pass_many=False)
125
+ def move_id(self, data, **kwargs):
126
+ """Moves id to pid."""
127
+ if "id" in data:
128
+ data["pid"] = data.pop("id")
129
+ return data
130
+
131
+ @pre_dump(pass_many=False)
132
+ def extract_pid_value(self, data, **kwargs):
133
+ """Extracts the PID value."""
134
+ data["id"] = data.pid.pid_value
135
+ return data
136
+
137
+
101
138
  class DatastreamObject(Schema):
102
139
  """Datastream object (reader, transformer, writer)."""
103
140
 
@@ -1,7 +1,8 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2020-2021 CERN.
3
+ # Copyright (C) 2024 CERN.
4
4
  # Copyright (C) 2021 Northwestern University.
5
+ # Copyright (C) 2024 University of Münster.
5
6
  #
6
7
  # Invenio-Vocabularies is free software; you can redistribute it and/or
7
8
  # modify it under the terms of the MIT License; see LICENSE file for more
@@ -9,108 +10,59 @@
9
10
 
10
11
  """Vocabulary service."""
11
12
 
13
+ import sqlalchemy as sa
12
14
  from invenio_cache import current_cache
13
- from invenio_db import db
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.records.components import DataComponent
24
- from invenio_records_resources.services.records.params import (
25
- FilterParam,
26
- SuggestQueryParser,
27
- )
15
+ from invenio_records_resources.services import LinksTemplate, RecordService
16
+ from invenio_records_resources.services.base.utils import map_search_params
28
17
  from invenio_records_resources.services.records.schema import ServiceSchemaWrapper
29
18
  from invenio_records_resources.services.uow import unit_of_work
30
19
  from invenio_search.engine import dsl
31
20
 
32
- from ..records.api import Vocabulary
33
21
  from ..records.models import VocabularyType
34
- from .components import PIDComponent, VocabularyTypeComponent
35
- from .permissions import PermissionPolicy
36
- from .schema import TaskSchema, VocabularySchema
37
22
  from .tasks import process_datastream
38
23
 
39
24
 
40
- class VocabularySearchOptions(SearchOptions):
41
- """Search options."""
42
-
43
- params_interpreters_cls = [
44
- FilterParam.factory(param="tags", field="tags"),
45
- ] + SearchOptions.params_interpreters_cls
46
-
47
- suggest_parser_cls = SuggestQueryParser.factory(
48
- fields=[
49
- "id.text^100",
50
- "id.text._2gram",
51
- "id.text._3gram",
52
- "title.en^5",
53
- "title.en._2gram",
54
- "title.en._3gram",
55
- ],
56
- )
57
-
58
- sort_default = "bestmatch"
59
-
60
- sort_default_no_query = "title"
61
-
62
- sort_options = {
63
- "bestmatch": dict(
64
- title=_("Best match"),
65
- fields=["_score"], # ES defaults to desc on `_score` field
66
- ),
67
- "title": dict(
68
- title=_("Title"),
69
- fields=["title_sort"],
70
- ),
71
- "newest": dict(
72
- title=_("Newest"),
73
- fields=["-created"],
74
- ),
75
- "oldest": dict(
76
- title=_("Oldest"),
77
- fields=["created"],
78
- ),
79
- }
80
-
81
-
82
- class VocabulariesServiceConfig(RecordServiceConfig):
83
- """Vocabulary service configuration."""
84
-
85
- service_id = "vocabularies"
86
- indexer_queue_name = "vocabularies"
87
- permission_policy_cls = PermissionPolicy
88
- record_cls = Vocabulary
89
- schema = VocabularySchema
90
- task_schema = TaskSchema
91
-
92
- search = VocabularySearchOptions
93
-
94
- components = [
95
- # Order of components are important!
96
- VocabularyTypeComponent,
97
- DataComponent,
98
- PIDComponent,
99
- ]
100
-
101
- links_item = {
102
- "self": Link(
103
- "{+api}/vocabularies/{type}/{id}",
104
- vars=lambda record, vars: vars.update(
105
- {
106
- "id": record.pid.pid_value,
107
- "type": record.type.id,
108
- }
109
- ),
110
- ),
111
- }
25
+ class VocabularyTypeService(RecordService):
26
+ """Vocabulary type service."""
27
+
28
+ def rebuild_index(self, identity, uow=None):
29
+ """Raise error since services are not backed by search indices."""
30
+ raise NotImplementedError()
31
+
32
+ def search(self, identity, params=None):
33
+ """Search for vocabulary types entries."""
34
+ self.require_permission(identity, "list_vocabularies")
35
+
36
+ search_params = map_search_params(self.config.search, params)
37
+
38
+ query_param = search_params["q"]
39
+
40
+ filters = []
41
+ if query_param:
42
+ filters.extend([VocabularyType.id.ilike(f"%{query_param}%")])
43
+
44
+ vocabulary_types = (
45
+ VocabularyType.query.filter(sa.or_(*filters))
46
+ .order_by(
47
+ search_params["sort_direction"](
48
+ sa.text(",".join(search_params["sort"]))
49
+ )
50
+ )
51
+ .paginate(
52
+ page=search_params["page"],
53
+ per_page=search_params["size"],
54
+ error_out=False,
55
+ )
56
+ )
112
57
 
113
- links_search = pagination_links("{+api}/vocabularies/{type}{?args*}")
58
+ return self.config.result_list_cls(
59
+ self,
60
+ identity,
61
+ vocabulary_types,
62
+ search_params,
63
+ links_tpl=LinksTemplate(self.config.links_search, context={"args": params}),
64
+ links_item_tpl=self.links_item_tpl,
65
+ )
114
66
 
115
67
 
116
68
  class VocabulariesService(RecordService):
@@ -145,7 +97,7 @@ class VocabulariesService(RecordService):
145
97
  params,
146
98
  search_preference,
147
99
  extra_filter=dsl.Q("term", type__id=vocabulary_type.id),
148
- **kwargs
100
+ **kwargs,
149
101
  ).execute()
150
102
 
151
103
  return self.result_list(
@@ -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-Vocabularies 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.
@@ -0,0 +1,23 @@
1
+ {#
2
+ Copyright (C) 2024 CERN.
3
+
4
+ Invenio RDM Records is free software; you can redistribute it and/or modify
5
+ it under the terms of the MIT License; see LICENSE file for more details.
6
+ #}
7
+
8
+ {%- set subjects = field_value -%}
9
+ {% if subjects %}
10
+ <ul class="ui horizontal list no-bullets subjects">
11
+ {%- for subject in subjects %}
12
+ <li class="item">
13
+ {%- set q = 'metadata.subjects.subject:"' + subject.subject + '"' %}
14
+ <a href="{{ url_for('invenio_search_ui.search', q=q) }}"
15
+ class="subject"
16
+ title="{{ _('Search results for ') + subject.subject }}"
17
+ >
18
+ {{ subject.subject }}
19
+ </a>
20
+ </li>
21
+ {%- endfor %}
22
+ </ul>
23
+ {% endif %}
@@ -0,0 +1,12 @@
1
+ {#
2
+ Copyright (C) 2024 Uni Münster.
3
+
4
+ Invenio App RDM is free software; you can redistribute it and/or modify it
5
+ under the terms of the MIT License; see LICENSE file for more details.
6
+ #}
7
+ {% extends "invenio_administration/search.html" %}
8
+
9
+ {% block javascript %}
10
+ {{ super() }}
11
+ {{ webpack['invenio-vocabularies-search.js'] }}
12
+ {% endblock %}