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.
- invenio_vocabularies/__init__.py +16 -0
- invenio_vocabularies/administration/__init__.py +10 -0
- invenio_vocabularies/administration/views/__init__.py +10 -0
- invenio_vocabularies/administration/views/vocabularies.py +43 -0
- invenio_vocabularies/alembic/17c703ce1eb7_create_names_table.py +54 -0
- invenio_vocabularies/alembic/3ba812d80559_add_internal_name_id.py +36 -0
- invenio_vocabularies/alembic/4a9a4fd235f8_create_vocabulary_schemes.py +37 -0
- invenio_vocabularies/alembic/4f365fced43f_create_vocabularies_tables.py +92 -0
- invenio_vocabularies/alembic/55a700f897b6_add_names_and_afiliations_pid_column.py +96 -0
- invenio_vocabularies/alembic/6312f33645c1_create_affiliations_table.py +54 -0
- invenio_vocabularies/alembic/676dd587542d_create_funders_vocabulary_table.py +58 -0
- invenio_vocabularies/alembic/8ff82dfb0be8_create_vocabularies_branch.py +28 -0
- invenio_vocabularies/alembic/__init__.py +9 -0
- invenio_vocabularies/alembic/af2457652217_drop_unique_constraint_from_internal_id.py +37 -0
- invenio_vocabularies/alembic/e1146238edd3_create_awards_table.py +56 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/.eslintrc.yml +11 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/.prettierrc +1 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/index.js +7 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/package.json +25 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/AwardResults.js +95 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js +139 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FunderDropdown.js +87 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.js +244 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.test.js +1 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingFieldItem.js +152 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingModal.js +246 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/NoAwardResults.js +37 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/index.js +8 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/index.js +7 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/index.js +7 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/index.js +7 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/i18next.js +36 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/_generatedTranslations.js +66 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ar/messages.po +96 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ar/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/bg/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/bg/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ca/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ca/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/cs/messages.po +97 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/cs/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/da/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/da/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/de/messages.po +98 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/de/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/el/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/el/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/en/messages.po +88 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/en/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/es/messages.po +96 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/es/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/et/messages.po +95 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/et/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/fa/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/fa/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/fr/messages.po +96 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/fr/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/hr/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/hr/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/hu/messages.po +96 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/hu/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/index.js +24 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/it/messages.po +96 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/it/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ja/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ja/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ka/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ka/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ko/messages.po +90 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ko/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/lt/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/lt/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/no/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/no/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/pl/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/pl/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/pt/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/pt/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ro/messages.po +95 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ro/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ru/messages.po +95 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/ru/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/sk/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/sk/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/sv/messages.po +98 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/sv/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/tr/messages.po +96 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/tr/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/uk/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/uk/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/zh_CN/messages.po +96 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/zh_CN/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/zh_TW/messages.po +94 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/messages/zh_TW/translations.json +28 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/package.json +19 -0
- invenio_vocabularies/assets/semantic-ui/translations/invenio_vocabularies/translations.pot +88 -0
- invenio_vocabularies/cli.py +175 -0
- invenio_vocabularies/config.py +231 -0
- invenio_vocabularies/contrib/__init__.py +9 -0
- invenio_vocabularies/contrib/affiliations/__init__.py +20 -0
- invenio_vocabularies/contrib/affiliations/affiliations.py +61 -0
- invenio_vocabularies/contrib/affiliations/api.py +13 -0
- invenio_vocabularies/contrib/affiliations/config.py +79 -0
- invenio_vocabularies/contrib/affiliations/datastreams.py +301 -0
- invenio_vocabularies/contrib/affiliations/facets.py +36 -0
- invenio_vocabularies/contrib/affiliations/jsonschemas/__init__.py +9 -0
- invenio_vocabularies/contrib/affiliations/jsonschemas/affiliations/affiliation-v1.0.0.json +63 -0
- invenio_vocabularies/contrib/affiliations/mappings/__init__.py +10 -0
- invenio_vocabularies/contrib/affiliations/mappings/os-v1/__init__.py +9 -0
- invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v1.0.0.json +112 -0
- invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v2.0.0.json +171 -0
- invenio_vocabularies/contrib/affiliations/mappings/os-v2/__init__.py +9 -0
- invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v1.0.0.json +112 -0
- invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v2.0.0.json +171 -0
- invenio_vocabularies/contrib/affiliations/mappings/v7/__init__.py +9 -0
- invenio_vocabularies/contrib/affiliations/mappings/v7/affiliations/affiliation-v1.0.0.json +112 -0
- invenio_vocabularies/contrib/affiliations/models.py +13 -0
- invenio_vocabularies/contrib/affiliations/resources.py +16 -0
- invenio_vocabularies/contrib/affiliations/schema.py +71 -0
- invenio_vocabularies/contrib/affiliations/services.py +15 -0
- invenio_vocabularies/contrib/awards/__init__.py +19 -0
- invenio_vocabularies/contrib/awards/api.py +13 -0
- invenio_vocabularies/contrib/awards/awards.py +96 -0
- invenio_vocabularies/contrib/awards/config.py +59 -0
- invenio_vocabularies/contrib/awards/datastreams.py +372 -0
- invenio_vocabularies/contrib/awards/jsonschemas/__init__.py +9 -0
- invenio_vocabularies/contrib/awards/jsonschemas/awards/award-v1.0.0.json +91 -0
- invenio_vocabularies/contrib/awards/mappings/__init__.py +9 -0
- invenio_vocabularies/contrib/awards/mappings/os-v1/__init__.py +9 -0
- invenio_vocabularies/contrib/awards/mappings/os-v1/awards/award-v1.0.0.json +147 -0
- invenio_vocabularies/contrib/awards/mappings/os-v2/__init__.py +9 -0
- invenio_vocabularies/contrib/awards/mappings/os-v2/awards/award-v1.0.0.json +147 -0
- invenio_vocabularies/contrib/awards/mappings/v7/__init__.py +9 -0
- invenio_vocabularies/contrib/awards/mappings/v7/awards/award-v1.0.0.json +147 -0
- invenio_vocabularies/contrib/awards/models.py +13 -0
- invenio_vocabularies/contrib/awards/resources.py +16 -0
- invenio_vocabularies/contrib/awards/schema.py +119 -0
- invenio_vocabularies/contrib/awards/serializer.py +47 -0
- invenio_vocabularies/contrib/awards/services.py +15 -0
- invenio_vocabularies/contrib/common/__init__.py +9 -0
- invenio_vocabularies/contrib/common/openaire/__init__.py +9 -0
- invenio_vocabularies/contrib/common/openaire/datastreams.py +84 -0
- invenio_vocabularies/contrib/common/ror/__init__.py +9 -0
- invenio_vocabularies/contrib/common/ror/datastreams.py +230 -0
- invenio_vocabularies/contrib/funders/__init__.py +19 -0
- invenio_vocabularies/contrib/funders/api.py +13 -0
- invenio_vocabularies/contrib/funders/config.py +78 -0
- invenio_vocabularies/contrib/funders/datastreams.py +97 -0
- invenio_vocabularies/contrib/funders/facets.py +36 -0
- invenio_vocabularies/contrib/funders/funders.py +72 -0
- invenio_vocabularies/contrib/funders/jsonschemas/__init__.py +9 -0
- invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json +65 -0
- invenio_vocabularies/contrib/funders/mappings/__init__.py +9 -0
- invenio_vocabularies/contrib/funders/mappings/os-v1/__init__.py +9 -0
- invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json +90 -0
- invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v2.0.0.json +156 -0
- invenio_vocabularies/contrib/funders/mappings/os-v2/__init__.py +9 -0
- invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json +90 -0
- invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v2.0.0.json +156 -0
- invenio_vocabularies/contrib/funders/mappings/v7/__init__.py +9 -0
- invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json +90 -0
- invenio_vocabularies/contrib/funders/models.py +13 -0
- invenio_vocabularies/contrib/funders/resources.py +16 -0
- invenio_vocabularies/contrib/funders/schema.py +88 -0
- invenio_vocabularies/contrib/funders/serializer.py +33 -0
- invenio_vocabularies/contrib/funders/services.py +15 -0
- invenio_vocabularies/contrib/names/__init__.py +19 -0
- invenio_vocabularies/contrib/names/api.py +13 -0
- invenio_vocabularies/contrib/names/components.py +24 -0
- invenio_vocabularies/contrib/names/config.py +75 -0
- invenio_vocabularies/contrib/names/datastreams.py +483 -0
- invenio_vocabularies/contrib/names/jsonschemas/__init__.py +9 -0
- invenio_vocabularies/contrib/names/jsonschemas/names/name-v1.0.0.json +68 -0
- invenio_vocabularies/contrib/names/mappings/__init__.py +9 -0
- invenio_vocabularies/contrib/names/mappings/os-v1/__init__.py +9 -0
- invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v1.0.0.json +101 -0
- invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v2.0.0.json +165 -0
- invenio_vocabularies/contrib/names/mappings/os-v2/__init__.py +9 -0
- invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v1.0.0.json +101 -0
- invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v2.0.0.json +165 -0
- invenio_vocabularies/contrib/names/mappings/v7/__init__.py +9 -0
- invenio_vocabularies/contrib/names/mappings/v7/names/name-v1.0.0.json +101 -0
- invenio_vocabularies/contrib/names/models.py +13 -0
- invenio_vocabularies/contrib/names/names.py +80 -0
- invenio_vocabularies/contrib/names/permissions.py +30 -0
- invenio_vocabularies/contrib/names/resources.py +54 -0
- invenio_vocabularies/contrib/names/s3client.py +50 -0
- invenio_vocabularies/contrib/names/schema.py +121 -0
- invenio_vocabularies/contrib/names/services.py +64 -0
- invenio_vocabularies/contrib/subjects/__init__.py +22 -0
- invenio_vocabularies/contrib/subjects/api.py +14 -0
- invenio_vocabularies/contrib/subjects/config.py +90 -0
- invenio_vocabularies/contrib/subjects/datastreams.py +63 -0
- invenio_vocabularies/contrib/subjects/euroscivoc/__init__.py +9 -0
- invenio_vocabularies/contrib/subjects/euroscivoc/datastreams.py +101 -0
- invenio_vocabularies/contrib/subjects/facets.py +23 -0
- invenio_vocabularies/contrib/subjects/gemet/__init__.py +9 -0
- invenio_vocabularies/contrib/subjects/gemet/datastreams.py +140 -0
- invenio_vocabularies/contrib/subjects/jsonschemas/__init__.py +10 -0
- invenio_vocabularies/contrib/subjects/jsonschemas/subjects/subject-v1.0.0.json +69 -0
- invenio_vocabularies/contrib/subjects/mappings/__init__.py +9 -0
- invenio_vocabularies/contrib/subjects/mappings/os-v1/__init__.py +9 -0
- invenio_vocabularies/contrib/subjects/mappings/os-v1/subjects/subject-v1.0.0.json +96 -0
- invenio_vocabularies/contrib/subjects/mappings/os-v2/__init__.py +9 -0
- invenio_vocabularies/contrib/subjects/mappings/os-v2/subjects/subject-v1.0.0.json +96 -0
- invenio_vocabularies/contrib/subjects/mappings/v7/__init__.py +9 -0
- invenio_vocabularies/contrib/subjects/mappings/v7/subjects/subject-v1.0.0.json +96 -0
- invenio_vocabularies/contrib/subjects/mesh/__init__.py +9 -0
- invenio_vocabularies/contrib/subjects/mesh/datastreams.py +48 -0
- invenio_vocabularies/contrib/subjects/models.py +14 -0
- invenio_vocabularies/contrib/subjects/nvs/__init__.py +9 -0
- invenio_vocabularies/contrib/subjects/nvs/datastreams.py +114 -0
- invenio_vocabularies/contrib/subjects/resources.py +17 -0
- invenio_vocabularies/contrib/subjects/schema.py +101 -0
- invenio_vocabularies/contrib/subjects/services.py +30 -0
- invenio_vocabularies/contrib/subjects/subjects.py +55 -0
- invenio_vocabularies/datastreams/__init__.py +18 -0
- invenio_vocabularies/datastreams/datastreams.py +239 -0
- invenio_vocabularies/datastreams/errors.py +29 -0
- invenio_vocabularies/datastreams/factories.py +86 -0
- invenio_vocabularies/datastreams/readers.py +448 -0
- invenio_vocabularies/datastreams/tasks.py +115 -0
- invenio_vocabularies/datastreams/transformers.py +130 -0
- invenio_vocabularies/datastreams/writers.py +222 -0
- invenio_vocabularies/datastreams/xml.py +34 -0
- invenio_vocabularies/ext.py +179 -0
- invenio_vocabularies/factories.py +193 -0
- invenio_vocabularies/fixtures.py +52 -0
- invenio_vocabularies/jobs.py +207 -0
- invenio_vocabularies/proxies.py +27 -0
- invenio_vocabularies/records/__init__.py +9 -0
- invenio_vocabularies/records/api.py +53 -0
- invenio_vocabularies/records/jsonschemas/__init__.py +9 -0
- invenio_vocabularies/records/jsonschemas/vocabularies/definitions-v1.0.0.json +30 -0
- invenio_vocabularies/records/jsonschemas/vocabularies/vocabulary-v1.0.0.json +55 -0
- invenio_vocabularies/records/mappings/__init__.py +9 -0
- invenio_vocabularies/records/mappings/os-v1/__init__.py +9 -0
- invenio_vocabularies/records/mappings/os-v1/vocabularies/vocabulary-v1.0.0.json +109 -0
- invenio_vocabularies/records/mappings/os-v2/__init__.py +9 -0
- invenio_vocabularies/records/mappings/os-v2/vocabularies/vocabulary-v1.0.0.json +109 -0
- invenio_vocabularies/records/mappings/v7/__init__.py +9 -0
- invenio_vocabularies/records/mappings/v7/vocabularies/vocabulary-v1.0.0.json +109 -0
- invenio_vocabularies/records/models.py +90 -0
- invenio_vocabularies/records/pidprovider.py +118 -0
- invenio_vocabularies/records/systemfields/__init__.py +16 -0
- invenio_vocabularies/records/systemfields/pid.py +125 -0
- invenio_vocabularies/records/systemfields/relations.py +51 -0
- invenio_vocabularies/resources/__init__.py +23 -0
- invenio_vocabularies/resources/config.py +105 -0
- invenio_vocabularies/resources/resource.py +156 -0
- invenio_vocabularies/resources/schema.py +21 -0
- invenio_vocabularies/resources/serializer.py +39 -0
- invenio_vocabularies/services/__init__.py +19 -0
- invenio_vocabularies/services/components.py +58 -0
- invenio_vocabularies/services/config.py +173 -0
- invenio_vocabularies/services/custom_fields/__init__.py +17 -0
- invenio_vocabularies/services/custom_fields/subject.py +82 -0
- invenio_vocabularies/services/custom_fields/vocabulary.py +96 -0
- invenio_vocabularies/services/facets.py +114 -0
- invenio_vocabularies/services/generators.py +38 -0
- invenio_vocabularies/services/permissions.py +30 -0
- invenio_vocabularies/services/querystr.py +57 -0
- invenio_vocabularies/services/results.py +110 -0
- invenio_vocabularies/services/schema.py +163 -0
- invenio_vocabularies/services/service.py +189 -0
- invenio_vocabularies/services/tasks.py +38 -0
- invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/subjects.html +23 -0
- invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabularies-list.html +12 -0
- invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabulary-details.html +71 -0
- invenio_vocabularies/translations/ar/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/ar/LC_MESSAGES/messages.po +277 -0
- invenio_vocabularies/translations/bg/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/bg/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/ca/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/ca/LC_MESSAGES/messages.po +276 -0
- invenio_vocabularies/translations/cs/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/cs/LC_MESSAGES/messages.po +281 -0
- invenio_vocabularies/translations/da/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/da/LC_MESSAGES/messages.po +271 -0
- invenio_vocabularies/translations/de/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/de/LC_MESSAGES/messages.po +293 -0
- invenio_vocabularies/translations/el/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/el/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/es/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/es/LC_MESSAGES/messages.po +281 -0
- invenio_vocabularies/translations/et/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/et/LC_MESSAGES/messages.po +276 -0
- invenio_vocabularies/translations/fa/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/fa/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/fr/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/fr/LC_MESSAGES/messages.po +279 -0
- invenio_vocabularies/translations/hr/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/hr/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/hu/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/hu/LC_MESSAGES/messages.po +280 -0
- invenio_vocabularies/translations/it/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/it/LC_MESSAGES/messages.po +277 -0
- invenio_vocabularies/translations/ja/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/ja/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/ka/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/ka/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/ko/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/ko/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/lt/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/lt/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/messages.pot +270 -0
- invenio_vocabularies/translations/no/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/no/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/pl/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/pl/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/pt/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/pt/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/ro/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/ro/LC_MESSAGES/messages.po +280 -0
- invenio_vocabularies/translations/ru/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/ru/LC_MESSAGES/messages.po +276 -0
- invenio_vocabularies/translations/sk/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/sk/LC_MESSAGES/messages.po +276 -0
- invenio_vocabularies/translations/sv/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/sv/LC_MESSAGES/messages.po +280 -0
- invenio_vocabularies/translations/tr/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/tr/LC_MESSAGES/messages.po +277 -0
- invenio_vocabularies/translations/uk/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/uk/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.po +276 -0
- invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.mo +0 -0
- invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.po +275 -0
- invenio_vocabularies/views.py +53 -0
- invenio_vocabularies/webpack.py +51 -0
- invenio_vocabularies-9.1.2.dist-info/METADATA +517 -0
- invenio_vocabularies-9.1.2.dist-info/RECORD +337 -0
- invenio_vocabularies-9.1.2.dist-info/WHEEL +6 -0
- invenio_vocabularies-9.1.2.dist-info/entry_points.txt +73 -0
- invenio_vocabularies-9.1.2.dist-info/licenses/AUTHORS.rst +13 -0
- invenio_vocabularies-9.1.2.dist-info/licenses/LICENSE +21 -0
- 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
|
+
)
|