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,114 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2024-2025 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
+ """NVS subjects datastreams, readers, transformers, and writers."""
10
+
11
+ from invenio_vocabularies.datastreams.errors import TransformerError
12
+ from invenio_vocabularies.datastreams.readers import RDFReader
13
+ from invenio_vocabularies.datastreams.transformers import RDFTransformer
14
+
15
+ from ..config import nvs_file_url
16
+
17
+ # Available with the "rdf" extra
18
+ try:
19
+ import rdflib
20
+ except ImportError:
21
+ rdflib = None
22
+
23
+
24
+ class NVSSubjectsTransformer(RDFTransformer):
25
+ """
26
+ Transformer class to convert NVS RDF data to a dictionary format.
27
+
28
+ Input:
29
+ - Relevant fields:
30
+ - `skos:notation`: Primary identifier for the concept.
31
+ - `skos:prefLabel`: Preferred labels with language codes.
32
+ - `skos:altLabel`: Alternative labels (optional).
33
+ - `skos:definition`: Definitions of the concept.
34
+ - `owl:deprecated`: Boolean flag indicating if the concept is deprecated.
35
+
36
+ Output:
37
+ - A dictionary with the following structure:
38
+ {
39
+ "id": "SDN:P01::SAGEMSFM", # NVS-specific parameter ID (skos:notation).
40
+ "scheme": "NVS-P01", # The scheme name indicating this is a collection P01 from NERC Vocabulary Server (NVS).
41
+ "subject": "AMSSedAge", # The alternative label (skos:altLabel), if available, or None.
42
+ "title": {
43
+ "en": "14C age of Foraminiferida" # English preferred label (skos:prefLabel).
44
+ },
45
+ "props": {
46
+ "definitions": "Accelerated mass spectrometry on picked tests", # Definition of subject (skos:definition).
47
+ },
48
+ "identifiers": [
49
+ {
50
+ "scheme": "url", # Type of identifier (URL).
51
+ "identifier": "http://vocab.nerc.ac.uk/collection/P01/current/SAGEMSFM" # URI of the concept.
52
+ }
53
+ ]
54
+ }
55
+ """
56
+
57
+ def _get_subject_data(self, rdf_graph, subject):
58
+ """Fetch all triples for a subject and organize them into a dictionary."""
59
+ data = {}
60
+ for predicate, obj in rdf_graph.predicate_objects(subject=subject):
61
+ predicate_name = str(predicate)
62
+ if predicate_name not in data:
63
+ data[predicate_name] = []
64
+ data[predicate_name].append(obj)
65
+ return data
66
+
67
+ def _transform_entry(self, subject, rdf_graph):
68
+ """Transform an entry to the required dictionary format."""
69
+ labels = self._get_labels(subject, rdf_graph)
70
+ subject_data = self._get_subject_data(rdf_graph, subject)
71
+ deprecated = subject_data.get(str(rdflib.namespace.OWL.deprecated), [False])
72
+ if deprecated and str(deprecated[0]).lower() == "true":
73
+ raise TransformerError(f"Skipping deprecated subject: {subject_data}")
74
+
75
+ notation = subject_data.get(str(self.skos_core.notation), [])
76
+ if notation:
77
+ id = str(notation[0])
78
+ else:
79
+ raise TransformerError(f"No id found for: {subject}")
80
+
81
+ pref_labels = [
82
+ obj for obj in subject_data.get(str(self.skos_core.prefLabel), [])
83
+ ]
84
+
85
+ subject_text = str(pref_labels[0]) if pref_labels else labels["en"]
86
+ definition = str(subject_data.get(str(self.skos_core.definition), [None])[0])
87
+
88
+ return {
89
+ "id": id,
90
+ "scheme": "NVS-P02",
91
+ "subject": subject_text,
92
+ "title": labels,
93
+ "props": {"definition": definition} if definition else {},
94
+ "identifiers": self._get_identifiers(subject),
95
+ }
96
+
97
+
98
+ # Configuration for datastream
99
+
100
+ VOCABULARIES_DATASTREAM_TRANSFORMERS = {"nvs-transformer": NVSSubjectsTransformer}
101
+
102
+ DATASTREAM_CONFIG = {
103
+ "readers": [
104
+ {
105
+ "type": "http",
106
+ "args": {
107
+ "origin": nvs_file_url,
108
+ },
109
+ },
110
+ {"type": "rdf"},
111
+ ],
112
+ "transformers": [{"type": "nvs-transformer"}],
113
+ "writers": [{"args": {"writer": {"type": "subjects-service"}}, "type": "async"}],
114
+ }
@@ -0,0 +1,17 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021 CERN.
4
+ # Copyright (C) 2021 Northwestern University.
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
+ """Test the affiliation vocabulary resources."""
11
+
12
+ from .subjects import record_type
13
+
14
+ SubjectsResourceConfig = record_type.resource_config_cls
15
+ SubjectsResourceConfig.routes["item"] = "/<path:pid_value>"
16
+
17
+ SubjectsResource = record_type.resource_cls
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021 Northwestern University.
4
+ # Copyright (C) 2021-2024 CERN.
5
+ # Copyright (C) 2024 University of Münster.
6
+ # Copyright (C) 2025 Graz University of Technology.
7
+ #
8
+ # Invenio-Vocabularies is free software; you can redistribute it and/or
9
+ # modify it under the terms of the MIT License; see LICENSE file for more
10
+ # details.
11
+
12
+ """Subjects schema."""
13
+
14
+ from functools import partial
15
+
16
+ from invenio_i18n import get_locale
17
+ from marshmallow import EXCLUDE, Schema, ValidationError, fields, pre_load, validate
18
+ from marshmallow_utils.fields import URL, IdentifierSet, SanitizedUnicode
19
+ from marshmallow_utils.schemas import IdentifierSchema
20
+
21
+ from ...services.schema import (
22
+ BaseVocabularySchema,
23
+ ContribVocabularyRelationSchema,
24
+ i18n_strings,
25
+ )
26
+ from .config import subject_schemes
27
+
28
+
29
+ class StringOrListOfStrings(fields.Raw):
30
+ """Custom field to handle both string and list of strings."""
31
+
32
+ # TODO: Move this to marshmallow-utils for broader type support.
33
+ def _deserialize(self, value, attr, data, **kwargs):
34
+ if isinstance(value, str):
35
+ return fields.String()._deserialize(value, attr, data, **kwargs)
36
+ elif isinstance(value, list) and all(isinstance(item, str) for item in value):
37
+ return [
38
+ fields.String()._deserialize(item, attr, data, **kwargs)
39
+ for item in value
40
+ ]
41
+ raise ValidationError("Invalid value. Must be a string or a list of strings.")
42
+
43
+
44
+ class SubjectSchema(BaseVocabularySchema):
45
+ """Service schema for subjects."""
46
+
47
+ # id in BaseRecordSchema is not required, but I don't see why it shouldn't
48
+ # be, while scheme and subject are required. So I am making it required
49
+ # here.
50
+ id = SanitizedUnicode(required=True)
51
+ scheme = SanitizedUnicode(required=True)
52
+ subject = SanitizedUnicode(required=True)
53
+ title = i18n_strings
54
+ props = fields.Dict(keys=SanitizedUnicode(), values=StringOrListOfStrings())
55
+ identifiers = IdentifierSet(
56
+ fields.Nested(
57
+ partial(
58
+ IdentifierSchema,
59
+ allowed_schemes=subject_schemes,
60
+ identifier_required=False,
61
+ )
62
+ )
63
+ )
64
+ synonyms = fields.List(SanitizedUnicode())
65
+
66
+ @pre_load
67
+ def add_subject_from_title(self, data, **kwargs):
68
+ """Add subject from title if not present."""
69
+ locale = get_locale().language
70
+ if "subject" not in data:
71
+ data["subject"] = data["title"].get(locale) or data["title"].values()[0]
72
+ return data
73
+
74
+
75
+ class SubjectRelationSchema(ContribVocabularyRelationSchema):
76
+ """Schema to define an optional subject relation in another schema."""
77
+
78
+ # If re-running an OpenAIRE awards update on existing awards which already have subjects,
79
+ # the subject entries will contains `scheme` and `props`, which are unknown since they are `dump_only`.
80
+ # This makes the update exclude unknown field and go through with the update.
81
+ class Meta:
82
+ """Metadata class."""
83
+
84
+ unknown = EXCLUDE
85
+
86
+ ftf_name = "subject"
87
+ parent_field_name = "subjects"
88
+ subject = SanitizedUnicode()
89
+ scheme = SanitizedUnicode(dump_only=True)
90
+ title = fields.Dict(dump_only=True)
91
+ props = fields.Dict(dump_only=True)
92
+ identifiers = IdentifierSet(
93
+ fields.Nested(
94
+ partial(
95
+ IdentifierSchema,
96
+ allowed_schemes=subject_schemes,
97
+ identifier_required=False,
98
+ )
99
+ )
100
+ )
101
+ synonyms = fields.List(SanitizedUnicode(), dump_only=True)
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021 CERN.
4
+ # Copyright (C) 2021 Northwestern University.
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
+ """Subjects services."""
11
+
12
+ from invenio_db import db
13
+
14
+ from ...records.models import VocabularyScheme
15
+ from .subjects import record_type
16
+
17
+ SubjectsServiceConfig = record_type.service_config_cls
18
+
19
+
20
+ class SubjectsService(record_type.service_cls):
21
+ """Subjects service."""
22
+
23
+ def create_scheme(self, identity, id_, name="", uri=""):
24
+ """Create a row for the subject scheme metadata."""
25
+ self.require_permission(identity, "manage")
26
+ scheme = VocabularyScheme.create(
27
+ id=id_, parent_id="subjects", name=name, uri=uri
28
+ )
29
+ db.session.commit()
30
+ return scheme
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2020-2021 CERN.
4
+ # Copyright (C) 2021 Northwestern University.
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 subjects."""
11
+
12
+ from flask_resources import JSONSerializer, ResponseHandler
13
+ from invenio_records.dumpers import SearchDumper
14
+ from invenio_records.dumpers.indexedat import IndexedAtDumperExt
15
+ from invenio_records_resources.factories.factory import RecordTypeFactory
16
+ from invenio_records_resources.resources.records.headers import etag_headers
17
+
18
+ from ...records.pidprovider import PIDProviderFactory
19
+ from ...records.systemfields import BaseVocabularyPIDFieldContext
20
+ from ...services.permissions import PermissionPolicy
21
+ from .config import SubjectsSearchOptions, service_components
22
+ from .schema import SubjectSchema
23
+
24
+ record_type = RecordTypeFactory(
25
+ "Subject",
26
+ # Data layer
27
+ pid_field_kwargs={
28
+ "create": False,
29
+ "provider": PIDProviderFactory.create(pid_type="sub"),
30
+ "context_cls": BaseVocabularyPIDFieldContext,
31
+ },
32
+ schema_version="1.0.0",
33
+ schema_path="local://subjects/subject-v1.0.0.json",
34
+ record_dumper=SearchDumper(
35
+ extensions=[
36
+ IndexedAtDumperExt(),
37
+ ]
38
+ ),
39
+ # Service layer
40
+ service_id="subjects",
41
+ service_schema=SubjectSchema,
42
+ search_options=SubjectsSearchOptions,
43
+ service_components=service_components,
44
+ permission_policy_cls=PermissionPolicy,
45
+ # Resource layer
46
+ endpoint_route="/subjects",
47
+ resource_cls_attrs={
48
+ "response_handlers": {
49
+ "application/json": ResponseHandler(JSONSerializer(), headers=etag_headers),
50
+ "application/vnd.inveniordm.v1+json": ResponseHandler(
51
+ JSONSerializer(), headers=etag_headers
52
+ ),
53
+ }
54
+ },
55
+ )
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021-2022 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
+ """Datastreams module."""
10
+
11
+ from .datastreams import DataStream, StreamEntry
12
+ from .factories import DataStreamFactory
13
+
14
+ __all__ = (
15
+ "DataStream",
16
+ "DataStreamFactory",
17
+ "StreamEntry",
18
+ )
@@ -0,0 +1,239 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021-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
+ """Base data stream."""
10
+
11
+ from flask import current_app
12
+ from invenio_access.permissions import system_identity, system_user_id
13
+ from invenio_access.utils import get_identity
14
+ from invenio_accounts.proxies import current_datastore
15
+ from invenio_jobs.logging.jobs import EMPTY_JOB_CTX, job_context
16
+ from invenio_jobs.proxies import current_runs_service
17
+
18
+ from .errors import ReaderError, TransformerError, WriterError
19
+
20
+
21
+ class StreamEntry:
22
+ """Object to encapsulate streams processing."""
23
+
24
+ def __init__(self, entry, record=None, errors=None, op_type=None, exc=None):
25
+ """Constructor for the StreamEntry class.
26
+
27
+ :param entry (object): The entry object, usually a record dict.
28
+ :param record (object): The record object, usually a record class.
29
+ :param errors (list, optional): List of errors. Defaults to None.
30
+ :param op_type (str, optional): The operation type. Defaults to None.
31
+ :param exc (str, optional): The raised unhandled exception. Defaults to None.
32
+ """
33
+ self.entry = entry
34
+ self.record = record
35
+ self.filtered = False
36
+ self.errors = errors or []
37
+ self.op_type = op_type
38
+ self.exc = exc
39
+
40
+ def log_errors(self, logger=None):
41
+ """Log the errors using the provided logger or the default logger.
42
+
43
+ :param logger (logging.Logger, optional): Logger instance to use. Defaults to None.
44
+ """
45
+ if logger is None:
46
+ logger = current_app.logger
47
+ for error in self.errors:
48
+ logger.error(f"Error in entry {self.entry}: {error}")
49
+ if self.exc:
50
+ logger.error(f"Exception in entry {self.entry}: {self.exc}")
51
+
52
+
53
+ class DataStream:
54
+ """Data stream."""
55
+
56
+ def __init__(
57
+ self,
58
+ readers,
59
+ writers,
60
+ transformers=None,
61
+ batch_size=100,
62
+ write_many=False,
63
+ *args,
64
+ **kwargs,
65
+ ):
66
+ """Constructor.
67
+
68
+ :param readers: an ordered list of readers.
69
+ :param writers: an ordered list of writers.
70
+ :param transformers: an ordered list of transformers to apply.
71
+ """
72
+ self._readers = readers
73
+ self._transformers = transformers
74
+ self._writers = writers
75
+ self.batch_size = batch_size
76
+ self.write_many = write_many
77
+
78
+ def filter(self, stream_entry, *args, **kwargs):
79
+ """Checks if an stream_entry should be filtered out (skipped)."""
80
+ current_app.logger.debug(f"Filtering entry: {stream_entry.entry}")
81
+ return False
82
+
83
+ def process_batch(self, batch):
84
+ """Process a batch of entries."""
85
+ current_app.logger.info(f"Processing batch of size: {len(batch)}")
86
+ if job_context.get() is not EMPTY_JOB_CTX:
87
+ run_id = job_context.get()["run_id"]
88
+ current_runs_service.add_total_entries(
89
+ system_identity,
90
+ run_id=run_id,
91
+ job_id=job_context.get()["job_id"],
92
+ total_entries=len(batch),
93
+ )
94
+ transformed_entries = []
95
+ transformed_entries_with_errors = []
96
+ for stream_entry in batch:
97
+ if stream_entry.errors:
98
+ current_app.logger.warning(
99
+ f"Skipping entry with errors: {stream_entry.errors}"
100
+ )
101
+ yield stream_entry # reading errors
102
+ else:
103
+ transformed_entry = self.transform(stream_entry)
104
+ if transformed_entry.errors:
105
+ transformed_entries_with_errors.append(transformed_entry)
106
+ yield transformed_entry
107
+ elif self.filter(transformed_entry):
108
+ transformed_entry.filtered = True
109
+ yield transformed_entry
110
+ else:
111
+ transformed_entries.append(transformed_entry)
112
+ if transformed_entries_with_errors:
113
+ current_app.logger.warning(
114
+ f"Skipping {len(transformed_entries_with_errors)} transformed entries with errors."
115
+ )
116
+ if transformed_entries:
117
+ if self.write_many:
118
+ yield from self.batch_write(transformed_entries)
119
+ else:
120
+ yield from (self.write(entry) for entry in transformed_entries)
121
+
122
+ def process(self, *args, **kwargs):
123
+ """Iterates over the entries.
124
+
125
+ Uses the reader to get the raw entries and transforms them.
126
+ It will iterate over the `StreamEntry` objects returned by
127
+ the reader, apply the transformations and yield the result of
128
+ writing it.
129
+ """
130
+ current_app.logger.info("Starting data stream processing")
131
+ batch = []
132
+ for stream_entry in self.read():
133
+ batch.append(stream_entry)
134
+ if len(batch) >= self.batch_size:
135
+ current_app.logger.debug(f"Processing batch of size: {len(batch)}")
136
+ yield from self.process_batch(batch)
137
+ batch = []
138
+
139
+ # Process any remaining entries in the last batch
140
+ if batch:
141
+ current_app.logger.debug(f"Processing final batch of size: {len(batch)}")
142
+ yield from self.process_batch(batch)
143
+
144
+ def read(self):
145
+ """Recursively read the entries."""
146
+ current_app.logger.debug("Reading entries from readers")
147
+
148
+ def pipe_gen(gen_funcs, piped_item=None):
149
+ _gen_funcs = list(gen_funcs) # copy to avoid modifying ref list
150
+ # use and remove the current generator
151
+ current_gen_func = _gen_funcs.pop(0)
152
+ for item in current_gen_func(piped_item):
153
+ try:
154
+ # exhaust iterations of subsequent generators
155
+ if _gen_funcs:
156
+ yield from pipe_gen(_gen_funcs, piped_item=item)
157
+ # there is no subsequent generator, return the current item
158
+ else:
159
+ yield StreamEntry(item)
160
+ except ReaderError as err:
161
+ current_app.logger.error(f"Reader error: {str(err)}")
162
+ yield StreamEntry(
163
+ entry=item,
164
+ errors=[f"{current_gen_func.__qualname__}: {str(err)}"],
165
+ )
166
+
167
+ read_gens = [r.read for r in self._readers]
168
+ yield from pipe_gen(read_gens)
169
+
170
+ def transform(self, stream_entry, *args, **kwargs):
171
+ """Apply the transformations to an stream_entry."""
172
+ current_app.logger.debug(f"Transforming entry: {stream_entry.entry}")
173
+ for transformer in self._transformers:
174
+ try:
175
+ stream_entry = transformer.apply(stream_entry)
176
+ except TransformerError as err:
177
+ stream_entry.errors.append(
178
+ f"{transformer.__class__.__name__}: {str(err)}"
179
+ )
180
+ return stream_entry # break loop
181
+
182
+ return stream_entry
183
+
184
+ def _prepare_async_context(self):
185
+ """Prepare the async context for writers."""
186
+ job_ctx = job_context.get()
187
+ run_id = job_ctx.get("run_id")
188
+ identity_id = job_ctx.get("identity_id")
189
+ job_id = job_ctx.get("job_id")
190
+ # System user needs to be handled separately because it doesn't exist in the database
191
+ if identity_id == system_user_id:
192
+ identity = system_identity
193
+ else:
194
+ user = current_datastore.get_user(identity_id)
195
+ if user is None:
196
+ raise ValueError(f"User with ID:{identity_id} not found.")
197
+ identity = get_identity(user)
198
+
199
+ subtask_run = current_runs_service.create_subtask_run(
200
+ identity, parent_run_id=run_id, job_id=job_id
201
+ )
202
+ return str(subtask_run.id)
203
+
204
+ def write(self, stream_entry, *args, **kwargs):
205
+ """Write a single stream entry."""
206
+ current_app.logger.debug(f"Writing entry: {stream_entry.entry}")
207
+ for writer in self._writers:
208
+ try:
209
+ if writer.is_async and job_context.get() is not EMPTY_JOB_CTX:
210
+ subtask_run_id = self._prepare_async_context()
211
+ writer.write(stream_entry, subtask_run_id=subtask_run_id)
212
+ else:
213
+ writer.write(stream_entry)
214
+ except WriterError as err:
215
+ current_app.logger.error(f"Writer error: {str(err)}")
216
+ stream_entry.errors.append(f"{writer.__class__.__name__}: {str(err)}")
217
+
218
+ return stream_entry
219
+
220
+ def batch_write(self, stream_entries, *args, **kwargs):
221
+ """Write a batch of stream entries."""
222
+ current_app.logger.debug(f"Batch writing entries: {len(stream_entries)}")
223
+ for writer in self._writers:
224
+ try:
225
+ if writer.is_async and job_context.get() is not EMPTY_JOB_CTX:
226
+ subtask_run_id = self._prepare_async_context()
227
+ yield from writer.write_many(
228
+ stream_entries, subtask_run_id=subtask_run_id
229
+ )
230
+ else:
231
+ yield from writer.write_many(stream_entries)
232
+ except WriterError as err:
233
+ current_app.logger.error(f"Writer error: {str(err)}")
234
+ for entry in stream_entries:
235
+ entry.errors.append(f"{writer.__class__.__name__}: {str(err)}")
236
+
237
+ def total(self, *args, **kwargs):
238
+ """The total of entries obtained from the origin."""
239
+ raise NotImplementedError()
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021 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
+ """Datastream errors."""
10
+
11
+
12
+ class ReaderError(Exception):
13
+ """Transformer application exception."""
14
+
15
+
16
+ class TransformerError(Exception):
17
+ """Transformer application exception."""
18
+
19
+
20
+ class WriterError(Exception):
21
+ """Transformer application exception."""
22
+
23
+
24
+ class FactoryError(Exception):
25
+ """Transformer application exception."""
26
+
27
+ def __init__(self, name, key):
28
+ """Initialise error."""
29
+ super().__init__(f"{name} {key} not configured.")