invenio-vocabularies 9.1.2__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.
Files changed (337) hide show
  1. invenio_vocabularies/__init__.py +16 -0
  2. invenio_vocabularies/administration/__init__.py +10 -0
  3. invenio_vocabularies/administration/views/__init__.py +10 -0
  4. invenio_vocabularies/administration/views/vocabularies.py +43 -0
  5. invenio_vocabularies/alembic/17c703ce1eb7_create_names_table.py +54 -0
  6. invenio_vocabularies/alembic/3ba812d80559_add_internal_name_id.py +36 -0
  7. invenio_vocabularies/alembic/4a9a4fd235f8_create_vocabulary_schemes.py +37 -0
  8. invenio_vocabularies/alembic/4f365fced43f_create_vocabularies_tables.py +92 -0
  9. invenio_vocabularies/alembic/55a700f897b6_add_names_and_afiliations_pid_column.py +96 -0
  10. invenio_vocabularies/alembic/6312f33645c1_create_affiliations_table.py +54 -0
  11. invenio_vocabularies/alembic/676dd587542d_create_funders_vocabulary_table.py +58 -0
  12. invenio_vocabularies/alembic/8ff82dfb0be8_create_vocabularies_branch.py +28 -0
  13. invenio_vocabularies/alembic/__init__.py +9 -0
  14. invenio_vocabularies/alembic/af2457652217_drop_unique_constraint_from_internal_id.py +37 -0
  15. invenio_vocabularies/alembic/e1146238edd3_create_awards_table.py +56 -0
  16. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/.eslintrc.yml +11 -0
  17. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/.prettierrc +1 -0
  18. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/index.js +7 -0
  19. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/package.json +25 -0
  20. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/AwardResults.js +95 -0
  21. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js +139 -0
  22. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FunderDropdown.js +87 -0
  23. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.js +244 -0
  24. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.test.js +1 -0
  25. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingFieldItem.js +152 -0
  26. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingModal.js +246 -0
  27. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/NoAwardResults.js +37 -0
  28. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/index.js +8 -0
  29. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/index.js +7 -0
  30. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/index.js +7 -0
  31. invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/index.js +7 -0
  32. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/i18next.js +36 -0
  33. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/_generatedTranslations.js +66 -0
  34. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ar/messages.po +96 -0
  35. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ar/translations.json +28 -0
  36. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/bg/messages.po +94 -0
  37. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/bg/translations.json +28 -0
  38. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ca/messages.po +94 -0
  39. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ca/translations.json +28 -0
  40. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/cs/messages.po +97 -0
  41. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/cs/translations.json +28 -0
  42. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/da/messages.po +94 -0
  43. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/da/translations.json +28 -0
  44. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/de/messages.po +98 -0
  45. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/de/translations.json +28 -0
  46. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/el/messages.po +94 -0
  47. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/el/translations.json +28 -0
  48. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/en/messages.po +88 -0
  49. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/en/translations.json +28 -0
  50. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/es/messages.po +96 -0
  51. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/es/translations.json +28 -0
  52. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/et/messages.po +95 -0
  53. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/et/translations.json +28 -0
  54. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/fa/messages.po +94 -0
  55. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/fa/translations.json +28 -0
  56. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/fr/messages.po +96 -0
  57. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/fr/translations.json +28 -0
  58. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/hr/messages.po +94 -0
  59. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/hr/translations.json +28 -0
  60. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/hu/messages.po +96 -0
  61. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/hu/translations.json +28 -0
  62. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/index.js +24 -0
  63. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/it/messages.po +96 -0
  64. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/it/translations.json +28 -0
  65. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ja/messages.po +94 -0
  66. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ja/translations.json +28 -0
  67. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ka/messages.po +94 -0
  68. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ka/translations.json +28 -0
  69. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ko/messages.po +90 -0
  70. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ko/translations.json +28 -0
  71. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/lt/messages.po +94 -0
  72. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/lt/translations.json +28 -0
  73. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/no/messages.po +94 -0
  74. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/no/translations.json +28 -0
  75. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/pl/messages.po +94 -0
  76. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/pl/translations.json +28 -0
  77. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/pt/messages.po +94 -0
  78. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/pt/translations.json +28 -0
  79. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ro/messages.po +95 -0
  80. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ro/translations.json +28 -0
  81. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ru/messages.po +95 -0
  82. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ru/translations.json +28 -0
  83. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/sk/messages.po +94 -0
  84. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/sk/translations.json +28 -0
  85. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/sv/messages.po +98 -0
  86. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/sv/translations.json +28 -0
  87. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/tr/messages.po +96 -0
  88. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/tr/translations.json +28 -0
  89. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/uk/messages.po +94 -0
  90. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/uk/translations.json +28 -0
  91. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/zh_CN/messages.po +96 -0
  92. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/zh_CN/translations.json +28 -0
  93. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/zh_TW/messages.po +94 -0
  94. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/zh_TW/translations.json +28 -0
  95. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/package.json +19 -0
  96. invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/translations.pot +88 -0
  97. invenio_vocabularies/cli.py +175 -0
  98. invenio_vocabularies/config.py +231 -0
  99. invenio_vocabularies/contrib/__init__.py +9 -0
  100. invenio_vocabularies/contrib/affiliations/__init__.py +20 -0
  101. invenio_vocabularies/contrib/affiliations/affiliations.py +61 -0
  102. invenio_vocabularies/contrib/affiliations/api.py +13 -0
  103. invenio_vocabularies/contrib/affiliations/config.py +79 -0
  104. invenio_vocabularies/contrib/affiliations/datastreams.py +301 -0
  105. invenio_vocabularies/contrib/affiliations/facets.py +36 -0
  106. invenio_vocabularies/contrib/affiliations/jsonschemas/__init__.py +9 -0
  107. invenio_vocabularies/contrib/affiliations/jsonschemas/affiliations/affiliation-v1.0.0.json +63 -0
  108. invenio_vocabularies/contrib/affiliations/mappings/__init__.py +10 -0
  109. invenio_vocabularies/contrib/affiliations/mappings/os-v1/__init__.py +9 -0
  110. invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v1.0.0.json +112 -0
  111. invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v2.0.0.json +171 -0
  112. invenio_vocabularies/contrib/affiliations/mappings/os-v2/__init__.py +9 -0
  113. invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v1.0.0.json +112 -0
  114. invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v2.0.0.json +171 -0
  115. invenio_vocabularies/contrib/affiliations/mappings/v7/__init__.py +9 -0
  116. invenio_vocabularies/contrib/affiliations/mappings/v7/affiliations/affiliation-v1.0.0.json +112 -0
  117. invenio_vocabularies/contrib/affiliations/models.py +13 -0
  118. invenio_vocabularies/contrib/affiliations/resources.py +16 -0
  119. invenio_vocabularies/contrib/affiliations/schema.py +71 -0
  120. invenio_vocabularies/contrib/affiliations/services.py +15 -0
  121. invenio_vocabularies/contrib/awards/__init__.py +19 -0
  122. invenio_vocabularies/contrib/awards/api.py +13 -0
  123. invenio_vocabularies/contrib/awards/awards.py +96 -0
  124. invenio_vocabularies/contrib/awards/config.py +59 -0
  125. invenio_vocabularies/contrib/awards/datastreams.py +372 -0
  126. invenio_vocabularies/contrib/awards/jsonschemas/__init__.py +9 -0
  127. invenio_vocabularies/contrib/awards/jsonschemas/awards/award-v1.0.0.json +91 -0
  128. invenio_vocabularies/contrib/awards/mappings/__init__.py +9 -0
  129. invenio_vocabularies/contrib/awards/mappings/os-v1/__init__.py +9 -0
  130. invenio_vocabularies/contrib/awards/mappings/os-v1/awards/award-v1.0.0.json +147 -0
  131. invenio_vocabularies/contrib/awards/mappings/os-v2/__init__.py +9 -0
  132. invenio_vocabularies/contrib/awards/mappings/os-v2/awards/award-v1.0.0.json +147 -0
  133. invenio_vocabularies/contrib/awards/mappings/v7/__init__.py +9 -0
  134. invenio_vocabularies/contrib/awards/mappings/v7/awards/award-v1.0.0.json +147 -0
  135. invenio_vocabularies/contrib/awards/models.py +13 -0
  136. invenio_vocabularies/contrib/awards/resources.py +16 -0
  137. invenio_vocabularies/contrib/awards/schema.py +119 -0
  138. invenio_vocabularies/contrib/awards/serializer.py +47 -0
  139. invenio_vocabularies/contrib/awards/services.py +15 -0
  140. invenio_vocabularies/contrib/common/__init__.py +9 -0
  141. invenio_vocabularies/contrib/common/openaire/__init__.py +9 -0
  142. invenio_vocabularies/contrib/common/openaire/datastreams.py +84 -0
  143. invenio_vocabularies/contrib/common/ror/__init__.py +9 -0
  144. invenio_vocabularies/contrib/common/ror/datastreams.py +230 -0
  145. invenio_vocabularies/contrib/funders/__init__.py +19 -0
  146. invenio_vocabularies/contrib/funders/api.py +13 -0
  147. invenio_vocabularies/contrib/funders/config.py +78 -0
  148. invenio_vocabularies/contrib/funders/datastreams.py +97 -0
  149. invenio_vocabularies/contrib/funders/facets.py +36 -0
  150. invenio_vocabularies/contrib/funders/funders.py +72 -0
  151. invenio_vocabularies/contrib/funders/jsonschemas/__init__.py +9 -0
  152. invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json +65 -0
  153. invenio_vocabularies/contrib/funders/mappings/__init__.py +9 -0
  154. invenio_vocabularies/contrib/funders/mappings/os-v1/__init__.py +9 -0
  155. invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json +90 -0
  156. invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v2.0.0.json +156 -0
  157. invenio_vocabularies/contrib/funders/mappings/os-v2/__init__.py +9 -0
  158. invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json +90 -0
  159. invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v2.0.0.json +156 -0
  160. invenio_vocabularies/contrib/funders/mappings/v7/__init__.py +9 -0
  161. invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json +90 -0
  162. invenio_vocabularies/contrib/funders/models.py +13 -0
  163. invenio_vocabularies/contrib/funders/resources.py +16 -0
  164. invenio_vocabularies/contrib/funders/schema.py +88 -0
  165. invenio_vocabularies/contrib/funders/serializer.py +33 -0
  166. invenio_vocabularies/contrib/funders/services.py +15 -0
  167. invenio_vocabularies/contrib/names/__init__.py +19 -0
  168. invenio_vocabularies/contrib/names/api.py +13 -0
  169. invenio_vocabularies/contrib/names/components.py +24 -0
  170. invenio_vocabularies/contrib/names/config.py +75 -0
  171. invenio_vocabularies/contrib/names/datastreams.py +483 -0
  172. invenio_vocabularies/contrib/names/jsonschemas/__init__.py +9 -0
  173. invenio_vocabularies/contrib/names/jsonschemas/names/name-v1.0.0.json +68 -0
  174. invenio_vocabularies/contrib/names/mappings/__init__.py +9 -0
  175. invenio_vocabularies/contrib/names/mappings/os-v1/__init__.py +9 -0
  176. invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v1.0.0.json +101 -0
  177. invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v2.0.0.json +165 -0
  178. invenio_vocabularies/contrib/names/mappings/os-v2/__init__.py +9 -0
  179. invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v1.0.0.json +101 -0
  180. invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v2.0.0.json +165 -0
  181. invenio_vocabularies/contrib/names/mappings/v7/__init__.py +9 -0
  182. invenio_vocabularies/contrib/names/mappings/v7/names/name-v1.0.0.json +101 -0
  183. invenio_vocabularies/contrib/names/models.py +13 -0
  184. invenio_vocabularies/contrib/names/names.py +80 -0
  185. invenio_vocabularies/contrib/names/permissions.py +30 -0
  186. invenio_vocabularies/contrib/names/resources.py +54 -0
  187. invenio_vocabularies/contrib/names/s3client.py +50 -0
  188. invenio_vocabularies/contrib/names/schema.py +121 -0
  189. invenio_vocabularies/contrib/names/services.py +64 -0
  190. invenio_vocabularies/contrib/subjects/__init__.py +22 -0
  191. invenio_vocabularies/contrib/subjects/api.py +14 -0
  192. invenio_vocabularies/contrib/subjects/config.py +90 -0
  193. invenio_vocabularies/contrib/subjects/datastreams.py +63 -0
  194. invenio_vocabularies/contrib/subjects/euroscivoc/__init__.py +9 -0
  195. invenio_vocabularies/contrib/subjects/euroscivoc/datastreams.py +101 -0
  196. invenio_vocabularies/contrib/subjects/facets.py +23 -0
  197. invenio_vocabularies/contrib/subjects/gemet/__init__.py +9 -0
  198. invenio_vocabularies/contrib/subjects/gemet/datastreams.py +140 -0
  199. invenio_vocabularies/contrib/subjects/jsonschemas/__init__.py +10 -0
  200. invenio_vocabularies/contrib/subjects/jsonschemas/subjects/subject-v1.0.0.json +69 -0
  201. invenio_vocabularies/contrib/subjects/mappings/__init__.py +9 -0
  202. invenio_vocabularies/contrib/subjects/mappings/os-v1/__init__.py +9 -0
  203. invenio_vocabularies/contrib/subjects/mappings/os-v1/subjects/subject-v1.0.0.json +96 -0
  204. invenio_vocabularies/contrib/subjects/mappings/os-v2/__init__.py +9 -0
  205. invenio_vocabularies/contrib/subjects/mappings/os-v2/subjects/subject-v1.0.0.json +96 -0
  206. invenio_vocabularies/contrib/subjects/mappings/v7/__init__.py +9 -0
  207. invenio_vocabularies/contrib/subjects/mappings/v7/subjects/subject-v1.0.0.json +96 -0
  208. invenio_vocabularies/contrib/subjects/mesh/__init__.py +9 -0
  209. invenio_vocabularies/contrib/subjects/mesh/datastreams.py +48 -0
  210. invenio_vocabularies/contrib/subjects/models.py +14 -0
  211. invenio_vocabularies/contrib/subjects/nvs/__init__.py +9 -0
  212. invenio_vocabularies/contrib/subjects/nvs/datastreams.py +114 -0
  213. invenio_vocabularies/contrib/subjects/resources.py +17 -0
  214. invenio_vocabularies/contrib/subjects/schema.py +101 -0
  215. invenio_vocabularies/contrib/subjects/services.py +30 -0
  216. invenio_vocabularies/contrib/subjects/subjects.py +55 -0
  217. invenio_vocabularies/datastreams/__init__.py +18 -0
  218. invenio_vocabularies/datastreams/datastreams.py +239 -0
  219. invenio_vocabularies/datastreams/errors.py +29 -0
  220. invenio_vocabularies/datastreams/factories.py +86 -0
  221. invenio_vocabularies/datastreams/readers.py +448 -0
  222. invenio_vocabularies/datastreams/tasks.py +115 -0
  223. invenio_vocabularies/datastreams/transformers.py +130 -0
  224. invenio_vocabularies/datastreams/writers.py +222 -0
  225. invenio_vocabularies/datastreams/xml.py +34 -0
  226. invenio_vocabularies/ext.py +179 -0
  227. invenio_vocabularies/factories.py +193 -0
  228. invenio_vocabularies/fixtures.py +52 -0
  229. invenio_vocabularies/jobs.py +207 -0
  230. invenio_vocabularies/proxies.py +27 -0
  231. invenio_vocabularies/records/__init__.py +9 -0
  232. invenio_vocabularies/records/api.py +53 -0
  233. invenio_vocabularies/records/jsonschemas/__init__.py +9 -0
  234. invenio_vocabularies/records/jsonschemas/vocabularies/definitions-v1.0.0.json +30 -0
  235. invenio_vocabularies/records/jsonschemas/vocabularies/vocabulary-v1.0.0.json +55 -0
  236. invenio_vocabularies/records/mappings/__init__.py +9 -0
  237. invenio_vocabularies/records/mappings/os-v1/__init__.py +9 -0
  238. invenio_vocabularies/records/mappings/os-v1/vocabularies/vocabulary-v1.0.0.json +109 -0
  239. invenio_vocabularies/records/mappings/os-v2/__init__.py +9 -0
  240. invenio_vocabularies/records/mappings/os-v2/vocabularies/vocabulary-v1.0.0.json +109 -0
  241. invenio_vocabularies/records/mappings/v7/__init__.py +9 -0
  242. invenio_vocabularies/records/mappings/v7/vocabularies/vocabulary-v1.0.0.json +109 -0
  243. invenio_vocabularies/records/models.py +90 -0
  244. invenio_vocabularies/records/pidprovider.py +118 -0
  245. invenio_vocabularies/records/systemfields/__init__.py +16 -0
  246. invenio_vocabularies/records/systemfields/pid.py +125 -0
  247. invenio_vocabularies/records/systemfields/relations.py +51 -0
  248. invenio_vocabularies/resources/__init__.py +23 -0
  249. invenio_vocabularies/resources/config.py +105 -0
  250. invenio_vocabularies/resources/resource.py +156 -0
  251. invenio_vocabularies/resources/schema.py +21 -0
  252. invenio_vocabularies/resources/serializer.py +39 -0
  253. invenio_vocabularies/services/__init__.py +19 -0
  254. invenio_vocabularies/services/components.py +58 -0
  255. invenio_vocabularies/services/config.py +173 -0
  256. invenio_vocabularies/services/custom_fields/__init__.py +17 -0
  257. invenio_vocabularies/services/custom_fields/subject.py +82 -0
  258. invenio_vocabularies/services/custom_fields/vocabulary.py +96 -0
  259. invenio_vocabularies/services/facets.py +114 -0
  260. invenio_vocabularies/services/generators.py +38 -0
  261. invenio_vocabularies/services/permissions.py +30 -0
  262. invenio_vocabularies/services/querystr.py +57 -0
  263. invenio_vocabularies/services/results.py +110 -0
  264. invenio_vocabularies/services/schema.py +163 -0
  265. invenio_vocabularies/services/service.py +189 -0
  266. invenio_vocabularies/services/tasks.py +38 -0
  267. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/subjects.html +23 -0
  268. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabularies-list.html +12 -0
  269. invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabulary-details.html +71 -0
  270. invenio_vocabularies/translations/ar/LC_MESSAGES/messages.mo +0 -0
  271. invenio_vocabularies/translations/ar/LC_MESSAGES/messages.po +277 -0
  272. invenio_vocabularies/translations/bg/LC_MESSAGES/messages.mo +0 -0
  273. invenio_vocabularies/translations/bg/LC_MESSAGES/messages.po +275 -0
  274. invenio_vocabularies/translations/ca/LC_MESSAGES/messages.mo +0 -0
  275. invenio_vocabularies/translations/ca/LC_MESSAGES/messages.po +276 -0
  276. invenio_vocabularies/translations/cs/LC_MESSAGES/messages.mo +0 -0
  277. invenio_vocabularies/translations/cs/LC_MESSAGES/messages.po +281 -0
  278. invenio_vocabularies/translations/da/LC_MESSAGES/messages.mo +0 -0
  279. invenio_vocabularies/translations/da/LC_MESSAGES/messages.po +271 -0
  280. invenio_vocabularies/translations/de/LC_MESSAGES/messages.mo +0 -0
  281. invenio_vocabularies/translations/de/LC_MESSAGES/messages.po +293 -0
  282. invenio_vocabularies/translations/el/LC_MESSAGES/messages.mo +0 -0
  283. invenio_vocabularies/translations/el/LC_MESSAGES/messages.po +275 -0
  284. invenio_vocabularies/translations/es/LC_MESSAGES/messages.mo +0 -0
  285. invenio_vocabularies/translations/es/LC_MESSAGES/messages.po +281 -0
  286. invenio_vocabularies/translations/et/LC_MESSAGES/messages.mo +0 -0
  287. invenio_vocabularies/translations/et/LC_MESSAGES/messages.po +276 -0
  288. invenio_vocabularies/translations/fa/LC_MESSAGES/messages.mo +0 -0
  289. invenio_vocabularies/translations/fa/LC_MESSAGES/messages.po +275 -0
  290. invenio_vocabularies/translations/fr/LC_MESSAGES/messages.mo +0 -0
  291. invenio_vocabularies/translations/fr/LC_MESSAGES/messages.po +279 -0
  292. invenio_vocabularies/translations/hr/LC_MESSAGES/messages.mo +0 -0
  293. invenio_vocabularies/translations/hr/LC_MESSAGES/messages.po +275 -0
  294. invenio_vocabularies/translations/hu/LC_MESSAGES/messages.mo +0 -0
  295. invenio_vocabularies/translations/hu/LC_MESSAGES/messages.po +280 -0
  296. invenio_vocabularies/translations/it/LC_MESSAGES/messages.mo +0 -0
  297. invenio_vocabularies/translations/it/LC_MESSAGES/messages.po +277 -0
  298. invenio_vocabularies/translations/ja/LC_MESSAGES/messages.mo +0 -0
  299. invenio_vocabularies/translations/ja/LC_MESSAGES/messages.po +275 -0
  300. invenio_vocabularies/translations/ka/LC_MESSAGES/messages.mo +0 -0
  301. invenio_vocabularies/translations/ka/LC_MESSAGES/messages.po +275 -0
  302. invenio_vocabularies/translations/ko/LC_MESSAGES/messages.mo +0 -0
  303. invenio_vocabularies/translations/ko/LC_MESSAGES/messages.po +275 -0
  304. invenio_vocabularies/translations/lt/LC_MESSAGES/messages.mo +0 -0
  305. invenio_vocabularies/translations/lt/LC_MESSAGES/messages.po +275 -0
  306. invenio_vocabularies/translations/messages.pot +270 -0
  307. invenio_vocabularies/translations/no/LC_MESSAGES/messages.mo +0 -0
  308. invenio_vocabularies/translations/no/LC_MESSAGES/messages.po +275 -0
  309. invenio_vocabularies/translations/pl/LC_MESSAGES/messages.mo +0 -0
  310. invenio_vocabularies/translations/pl/LC_MESSAGES/messages.po +275 -0
  311. invenio_vocabularies/translations/pt/LC_MESSAGES/messages.mo +0 -0
  312. invenio_vocabularies/translations/pt/LC_MESSAGES/messages.po +275 -0
  313. invenio_vocabularies/translations/ro/LC_MESSAGES/messages.mo +0 -0
  314. invenio_vocabularies/translations/ro/LC_MESSAGES/messages.po +280 -0
  315. invenio_vocabularies/translations/ru/LC_MESSAGES/messages.mo +0 -0
  316. invenio_vocabularies/translations/ru/LC_MESSAGES/messages.po +276 -0
  317. invenio_vocabularies/translations/sk/LC_MESSAGES/messages.mo +0 -0
  318. invenio_vocabularies/translations/sk/LC_MESSAGES/messages.po +276 -0
  319. invenio_vocabularies/translations/sv/LC_MESSAGES/messages.mo +0 -0
  320. invenio_vocabularies/translations/sv/LC_MESSAGES/messages.po +280 -0
  321. invenio_vocabularies/translations/tr/LC_MESSAGES/messages.mo +0 -0
  322. invenio_vocabularies/translations/tr/LC_MESSAGES/messages.po +277 -0
  323. invenio_vocabularies/translations/uk/LC_MESSAGES/messages.mo +0 -0
  324. invenio_vocabularies/translations/uk/LC_MESSAGES/messages.po +275 -0
  325. invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.mo +0 -0
  326. invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.po +276 -0
  327. invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.mo +0 -0
  328. invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.po +275 -0
  329. invenio_vocabularies/views.py +53 -0
  330. invenio_vocabularies/webpack.py +51 -0
  331. invenio_vocabularies-9.1.2.dist-info/METADATA +517 -0
  332. invenio_vocabularies-9.1.2.dist-info/RECORD +337 -0
  333. invenio_vocabularies-9.1.2.dist-info/WHEEL +6 -0
  334. invenio_vocabularies-9.1.2.dist-info/entry_points.txt +73 -0
  335. invenio_vocabularies-9.1.2.dist-info/licenses/AUTHORS.rst +13 -0
  336. invenio_vocabularies-9.1.2.dist-info/licenses/LICENSE +21 -0
  337. invenio_vocabularies-9.1.2.dist-info/top_level.txt +1 -0
@@ -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
+ }
@@ -0,0 +1,96 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2022-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
+ """Custom Fields for InvenioRDM."""
9
+
10
+ from invenio_records_resources.records.systemfields import PIDListRelation, PIDRelation
11
+ from invenio_records_resources.services.custom_fields.base import BaseCF
12
+ from marshmallow import fields
13
+
14
+ from ...proxies import current_service
15
+ from ...records.api import Vocabulary
16
+ from ...resources.serializer import VocabularyL10NItemSchema
17
+ from ...services.schema import VocabularyRelationSchema
18
+
19
+
20
+ class VocabularyCF(BaseCF):
21
+ """Vocabulary custom field.
22
+
23
+ Supporting common vocabulary structure.
24
+ """
25
+
26
+ field_keys = ["id", "props", "title", "icon"]
27
+ """Return field's keys for querying.
28
+
29
+ These keys are used to select which information to return from the
30
+ vocabulary that is queried.
31
+ """
32
+
33
+ def __init__(
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,
43
+ ):
44
+ """Constructor."""
45
+ super().__init__(name, **kwargs)
46
+ self.relation_cls = PIDRelation if not multiple else PIDListRelation
47
+ self.vocabulary_id = vocabulary_id
48
+ self.dump_options = dump_options
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)
54
+
55
+ @property
56
+ def mapping(self):
57
+ """Return the mapping."""
58
+ _mapping = {
59
+ "type": "object",
60
+ "properties": {
61
+ "@v": {"type": "keyword"},
62
+ "id": {"type": "keyword"},
63
+ "title": {"type": "object", "dynamic": "true"},
64
+ },
65
+ }
66
+
67
+ return _mapping
68
+
69
+ @property
70
+ def field(self):
71
+ """Marshmallow schema for vocabulary custom fields."""
72
+ return fields.Nested(self.schema, many=self.multiple, **self._field_args)
73
+
74
+ @property
75
+ def ui_field(self):
76
+ """Marshmallow UI schema for vocabulary custom fields.
77
+
78
+ This schema is used in the UIJSONSerializer and controls how the field will be
79
+ dumped in the UI. It takes responsibility of the localization of strings.
80
+ """
81
+ return fields.Nested(self.ui_schema, many=self.multiple, **self._field_args)
82
+
83
+ def options(self, identity):
84
+ """Return UI serialized vocabulary items."""
85
+ if self.dump_options:
86
+ vocabs = current_service.read_all(
87
+ identity,
88
+ fields=self.field_keys,
89
+ type=self.vocabulary_id,
90
+ sort=self.sort_by,
91
+ )
92
+ options = []
93
+ for vocab in vocabs:
94
+ options.append(VocabularyL10NItemSchema().dump(vocab))
95
+
96
+ return options
@@ -0,0 +1,114 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021-2023 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
+ """Vocabulary facets."""
10
+
11
+ from flask_principal import AnonymousIdentity
12
+ from invenio_cache.decorators import cached_with_expiration
13
+ from invenio_i18n.ext import current_i18n
14
+ from invenio_records_resources.proxies import current_service_registry
15
+ from invenio_records_resources.services.errors import FacetNotFoundError
16
+ from marshmallow_utils.fields.babel import gettext_from_dict
17
+ from speaklater import make_lazy_string
18
+ from sqlalchemy.exc import NoResultFound
19
+
20
+ from ..proxies import current_service
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
+
48
+ def lazy_get_label(vocab_item):
49
+ """Lazy evaluation of a localized vocabulary label."""
50
+ params = {"locale": current_i18n.locale, "default_locale": "en"}
51
+
52
+ return make_lazy_string(gettext_from_dict, vocab_item, **params)
53
+
54
+
55
+ class VocabularyLabels:
56
+ """Fetching of vocabulary labels for facets."""
57
+
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
+ """
70
+ self.vocabulary = vocabulary
71
+ self.cache = cache
72
+ self.cache_ttl = cache_ttl
73
+ self.fields = ("id", "title") # not configurable
74
+ self.service_id = service_id
75
+ self.id_field = id_field
76
+
77
+ def _vocab_to_label(self, vocab):
78
+ """Returns the label string for a vocabulary entry."""
79
+ return lazy_get_label(vocab["title"])
80
+
81
+ def __call__(self, ids):
82
+ """Return the mapping when evaluated."""
83
+ if not ids:
84
+ return {}
85
+
86
+ labels = {}
87
+ try:
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)
100
+ else:
101
+ vocab_list = get_vocabs(
102
+ self.service_id,
103
+ self.vocabulary,
104
+ self.fields,
105
+ ids,
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)
111
+ except NoResultFound:
112
+ raise FacetNotFoundError(self.vocabulary)
113
+
114
+ return labels
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 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
+
10
+ """Vocabulary generators."""
11
+
12
+ from invenio_access import any_user, authenticated_user
13
+ from invenio_records_permissions.generators import ConditionalGenerator
14
+ from invenio_search.engine import dsl
15
+
16
+
17
+ class IfTags(ConditionalGenerator):
18
+ """Generator to filter based on tags.
19
+
20
+ This generator will filter out records based on the tags field.
21
+ """
22
+
23
+ def __init__(self, tags, then_, else_):
24
+ """Constructor."""
25
+ self.tags = tags or []
26
+ super().__init__(then_, else_)
27
+
28
+ def _condition(self, record=None, **kwargs):
29
+ """Check if the record has the tags."""
30
+ return any(tag in record.get("tags", []) for tag in self.tags)
31
+
32
+ def query_filter(self, **kwargs):
33
+ """Search based on configured tags."""
34
+ must_not_clauses = [dsl.Q("terms", tags=self.tags)]
35
+ return dsl.Q(
36
+ "bool",
37
+ must_not=must_not_clauses,
38
+ )
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2020-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
+ """Vocabulary permissions."""
10
+
11
+ from invenio_records_permissions import RecordPermissionPolicy
12
+ from invenio_records_permissions.generators import AnyUser, SystemProcess
13
+
14
+ from invenio_vocabularies.services.generators import IfTags
15
+
16
+
17
+ class PermissionPolicy(RecordPermissionPolicy):
18
+ """Permission policy."""
19
+
20
+ can_search = [SystemProcess(), AnyUser()]
21
+ can_read = [
22
+ SystemProcess(),
23
+ IfTags(["unlisted"], then_=[SystemProcess()], else_=[AnyUser()]),
24
+ ]
25
+ can_create = [SystemProcess()]
26
+ can_update = [SystemProcess()]
27
+ can_delete = [SystemProcess()]
28
+ can_manage = [SystemProcess()]
29
+ # this permission is needed for the /api/vocabularies/ endpoint
30
+ can_list_vocabularies = [SystemProcess(), AnyUser()]
@@ -0,0 +1,57 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021 Northwestern University.
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
+ """Querystring parsing."""
10
+
11
+ import warnings
12
+ from functools import partial
13
+
14
+ from invenio_records_resources.services.records.params import SuggestQueryParser
15
+ from invenio_search.engine import dsl
16
+
17
+
18
+ class FilteredSuggestQueryParser(SuggestQueryParser):
19
+ """Query parser for filtered search-as-you-type/auto completion."""
20
+
21
+ @classmethod
22
+ def factory(cls, filter_field=None, **extra_params):
23
+ """Create a prepared instance of the query parser."""
24
+ warnings.warn(
25
+ "FilteredSuggestQueryParser is deprecated, use SuggestQueryParser or CompositeSuggestQueryParser instead",
26
+ DeprecationWarning,
27
+ )
28
+ return partial(cls, filter_field=filter_field, extra_params=extra_params)
29
+
30
+ def __init__(self, identity=None, filter_field=None, extra_params=None):
31
+ """Constructor."""
32
+ super().__init__(identity=identity, extra_params=extra_params)
33
+ self.filter_field = filter_field
34
+
35
+ def parse(self, query_str):
36
+ """Parse the query."""
37
+ subtype_s, query_str = self.extract_subtype_s(query_str)
38
+ query = super().parse(query_str)
39
+ if subtype_s:
40
+ query = query & dsl.Q("terms", **{self.filter_field: subtype_s})
41
+ return query
42
+
43
+ def extract_subtype_s(self, query_str):
44
+ """Extract the filtering subtype(s) from query_str.
45
+
46
+ Return (<subtypes>, <rest of original query string>).
47
+ """
48
+ parts = query_str.split(":", 1)
49
+ if len(parts) == 1:
50
+ subtypes = []
51
+ rest_query_str = query_str
52
+ else:
53
+ # Simplification: we can enforce no comma in subtype at
54
+ # subtype creation
55
+ subtypes = parts[0].split(",")
56
+ rest_query_str = parts[1]
57
+ return (subtypes, rest_query_str)
@@ -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}
@@ -0,0 +1,163 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2020-2024 CERN.
4
+ # Copyright (C) 2025 Graz University of Technology.
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 service schema."""
11
+
12
+ from invenio_i18n import lazy_gettext as _
13
+ from invenio_records_resources.services.records.schema import BaseRecordSchema
14
+ from marshmallow import (
15
+ Schema,
16
+ ValidationError,
17
+ fields,
18
+ post_load,
19
+ pre_dump,
20
+ pre_load,
21
+ validate,
22
+ validates_schema,
23
+ )
24
+ from marshmallow_utils.fields import SanitizedUnicode
25
+
26
+ i18n_strings = fields.Dict(
27
+ allow_none=False,
28
+ keys=fields.Str(validate=validate.Regexp("^[a-z]{2}$")),
29
+ values=fields.Str(),
30
+ )
31
+ """Field definition for language aware strings."""
32
+
33
+
34
+ class BaseVocabularyRelationSchema(Schema):
35
+ """Base Vocabulary relation schema."""
36
+
37
+ id = SanitizedUnicode(required=True)
38
+
39
+ # Nested field type for administration UI form generation
40
+ administration_schema_type = "vocabulary"
41
+
42
+
43
+ class VocabularyRelationSchema(BaseVocabularyRelationSchema):
44
+ """Vocabulary relation schema."""
45
+
46
+ title = fields.Dict(dump_only=True)
47
+
48
+ @pre_load
49
+ def clean(self, data, **kwargs):
50
+ """Removes dump_only fields.
51
+
52
+ Why: We want to allow the output of a Schema dump, to be a valid input
53
+ to a Schema load without causing strange issues.
54
+ """
55
+ value_is_dict = isinstance(data, dict)
56
+ if value_is_dict:
57
+ for name, field in self.fields.items():
58
+ if field.dump_only:
59
+ data.pop(name, None)
60
+ return data
61
+
62
+
63
+ class ContribVocabularyRelationSchema(Schema):
64
+ """Base Vocabulary relation schema."""
65
+
66
+ id = SanitizedUnicode()
67
+ ftf_name = None # free text field name
68
+ parent_field_name = None
69
+
70
+ # Nested field type for administration UI form generation
71
+ administration_schema_type = "vocabulary"
72
+
73
+ @validates_schema
74
+ def validate_relation_schema(self, data, **kwargs):
75
+ """Validates that either id either the free text field are present."""
76
+ id_ = data.get("id")
77
+ free_text = data.get(self.ftf_name)
78
+ if id_:
79
+ data = {"id": id_}
80
+ elif free_text:
81
+ data = {self.ftf_name: free_text}
82
+
83
+ if not id_ and not free_text:
84
+ raise ValidationError(
85
+ _(
86
+ "An existing id or a free text {ftf_name} must be present.".format(
87
+ ftf_name=self.ftf_name
88
+ )
89
+ ),
90
+ self.parent_field_name,
91
+ )
92
+
93
+
94
+ class BaseVocabularySchema(BaseRecordSchema):
95
+ """Base schema for vocabularies."""
96
+
97
+ title = i18n_strings
98
+ description = i18n_strings
99
+ icon = fields.Str(allow_none=False)
100
+ tags = fields.List(SanitizedUnicode())
101
+
102
+ # Nested field type for administration UI form generation
103
+ administration_schema_type = "vocabulary"
104
+
105
+
106
+ class VocabularySchema(BaseVocabularySchema):
107
+ """Service schema for vocabulary records."""
108
+
109
+ props = fields.Dict(allow_none=False, keys=fields.Str(), values=fields.Str())
110
+ type = fields.Str(attribute="type.id", required=True)
111
+
112
+
113
+ class ModePIDFieldVocabularyMixin:
114
+ """Mixin for vocabularies using a model field for their PID."""
115
+
116
+ @validates_schema
117
+ def validate_id(self, data, **kwargs):
118
+ """Validates ID."""
119
+ is_create = "record" not in self.context
120
+ if is_create and "id" not in data:
121
+ raise ValidationError(_("Missing PID."), "id")
122
+ if not is_create:
123
+ data.pop("id", None)
124
+
125
+ @post_load(pass_many=False)
126
+ def move_id(self, data, **kwargs):
127
+ """Moves id to pid."""
128
+ if "id" in data:
129
+ data["pid"] = data.pop("id")
130
+ return data
131
+
132
+ @pre_dump(pass_many=False)
133
+ def extract_pid_value(self, data, **kwargs):
134
+ """Extracts the PID value."""
135
+ data["id"] = data.pid.pid_value
136
+ return data
137
+
138
+
139
+ class DatastreamObject(Schema):
140
+ """Datastream object (reader, transformer, writer)."""
141
+
142
+ type = fields.Str(required=True)
143
+ args = fields.Dict(keys=fields.Str(), values=fields.Raw)
144
+
145
+
146
+ class TaskSchema(Schema):
147
+ """Service schema for vocabulary tasks."""
148
+
149
+ readers = fields.List(
150
+ fields.Nested(DatastreamObject),
151
+ validate=validate.Length(min=1),
152
+ required=True,
153
+ )
154
+ transformers = fields.List(
155
+ fields.Nested(DatastreamObject),
156
+ validate=validate.Length(min=1),
157
+ required=False,
158
+ )
159
+ writers = fields.List(
160
+ fields.Nested(DatastreamObject),
161
+ validate=validate.Length(min=1),
162
+ required=True,
163
+ )