From 6efd343ef22b1765156038ddc2f298e2b4b63a1c Mon Sep 17 00:00:00 2001 From: Fatimah Zulfiqar Date: Fri, 22 Nov 2024 15:47:32 +0100 Subject: [PATCH 1/2] affiliations: added custom scheme for edmo --- invenio.cfg | 100 ++++++++---------- site/setup.cfg | 2 + site/zenodo_rdm/custom_schemes.py | 57 ++++++++++ static/images/edmo-icon.svg | 3 + .../zenodo_rdm/macros/creators.html | 5 + 5 files changed, 111 insertions(+), 56 deletions(-) create mode 100644 site/zenodo_rdm/custom_schemes.py create mode 100644 static/images/edmo-icon.svg diff --git a/invenio.cfg b/invenio.cfg index 50929839..f687e9f3 100644 --- a/invenio.cfg +++ b/invenio.cfg @@ -20,73 +20,51 @@ from datetime import timedelta from celery.schedules import crontab from flask_resources import create_error_handler from invenio_administration.permissions import administration_permission -from invenio_app_rdm.config import ( - APP_RDM_DEPOSIT_FORM_DEFAULTS as DEPOSIT_FORM_DEFAULTS, -) -from invenio_app_rdm.config import ( - APP_RDM_ROUTES, - CELERY_BEAT_SCHEDULE, -) -from invenio_app_rdm.config import ( - COMMUNITIES_ERROR_HANDLERS as BASE_COMMUNITIES_ERROR_HANDLERS, -) +from invenio_app_rdm.config import \ + APP_RDM_DEPOSIT_FORM_DEFAULTS as DEPOSIT_FORM_DEFAULTS +from invenio_app_rdm.config import APP_RDM_ROUTES, CELERY_BEAT_SCHEDULE, APP_RDM_IDENTIFIER_SCHEMES_UI +from invenio_app_rdm.config import \ + COMMUNITIES_ERROR_HANDLERS as BASE_COMMUNITIES_ERROR_HANDLERS from invenio_communities.communities.services import facets as community_facets from invenio_github.oauth.remote_app import github_app as github_remote_app from invenio_i18n import lazy_gettext as _ from invenio_oauthclient.contrib.keycloak import KeycloakSettingsHelper -from invenio_oauthclient.contrib.orcid import ( - REMOTE_MEMBER_APP, - REMOTE_SANDBOX_MEMBER_APP, -) +from invenio_oauthclient.contrib.orcid import (REMOTE_MEMBER_APP, + REMOTE_SANDBOX_MEMBER_APP) from invenio_oauthclient.views.client import auto_redirect_login -from invenio_rdm_records.config import ( - RDM_FACETS, -) -from invenio_rdm_records.config import ( - RDM_SORT_OPTIONS as BASE_SORT_OPTIONS, -) +from invenio_rdm_records.config import (RDM_FACETS, + RDM_RECORDS_PERSONORG_SCHEMES) +from invenio_rdm_records.config import RDM_SORT_OPTIONS as BASE_SORT_OPTIONS from invenio_rdm_records.contrib.journal import JOURNAL_SORT_OPTIONS from invenio_rdm_records.contrib.meeting import MEETING_SORT_OPTIONS -from invenio_rdm_records.resources.config import ( - error_handlers as base_records_error_handlers, -) +from invenio_rdm_records.resources.config import \ + error_handlers as base_records_error_handlers from invenio_rdm_records.resources.errors import HTTPJSONException from invenio_rdm_records.services.components import DefaultRecordsComponents from invenio_rdm_records.services.components.signal import SignalComponent from invenio_records_resources.services.records.queryparser import ( - FieldValueMapper, - QueryParser, - SearchFieldTransformer, -) -from invenio_vocabularies.services.custom_fields import ( - SUBJECT_FIELDS, - SUBJECT_FIELDS_UI, -) + FieldValueMapper, QueryParser, SearchFieldTransformer) +from invenio_vocabularies.services.custom_fields import (SUBJECT_FIELDS, + SUBJECT_FIELDS_UI) from zenodo_rdm import facets as zenodo_community_facets from zenodo_rdm import providers as zenodo_providers from zenodo_rdm.api import ZenodoRDMDraft, ZenodoRDMRecord -from zenodo_rdm.custom_fields import ( - CUSTOM_FIELDS, - CUSTOM_FIELDS_FACETS, - CUSTOM_FIELDS_UI, - NAMESPACES, -) +from zenodo_rdm.custom_fields import (CUSTOM_FIELDS, CUSTOM_FIELDS_FACETS, + CUSTOM_FIELDS_UI, NAMESPACES) +from zenodo_rdm.custom_schemes import is_edmo from zenodo_rdm.files import storage_factory from zenodo_rdm.github.schemas import CitationMetadataSchema from zenodo_rdm.legacy.resources import record_serializers from zenodo_rdm.metrics.config import METRICS_CACHE_UPDATE_INTERVAL from zenodo_rdm.moderation.errors import UserBlockedException -from zenodo_rdm.moderation.handlers import CommunityScoreHandler, RecordScoreHandler +from zenodo_rdm.moderation.handlers import (CommunityScoreHandler, + RecordScoreHandler) from zenodo_rdm.openaire.records.components import OpenAIREComponent -from zenodo_rdm.permissions import ( - ZenodoCommunityPermissionPolicy, - ZenodoRDMRecordPermissionPolicy, -) +from zenodo_rdm.permissions import (ZenodoCommunityPermissionPolicy, + ZenodoRDMRecordPermissionPolicy) from zenodo_rdm.queryparser import word_communities, word_doi -from zenodo_rdm.subcommunities import ( - ZenodoSubCommunityRequest, - ZenodoSubcommunityRequestSchema, -) +from zenodo_rdm.subcommunities import (ZenodoSubCommunityRequest, + ZenodoSubcommunityRequestSchema) from zenodo_rdm.tokens import RATSubjectSchema from zenodo_rdm.views import frontpage_view_function @@ -739,16 +717,10 @@ USERPROFILES_READ_ONLY = ( THEME_SHOW_FRONTPAGE_INTRO_SECTION = False APP_RDM_RECORD_LANDING_PAGE_TEMPLATE = "zenodo_rdm/records/detail.html" -from zenodo_rdm.utils import ( - annostor_link_render, - blr_link_render, - briefideas_link_render, - f1000_link_render, - github_link_render, - openaire_link_render, - reana_link_render, - swh_link_render, -) +from zenodo_rdm.utils import (annostor_link_render, blr_link_render, + briefideas_link_render, f1000_link_render, + github_link_render, openaire_link_render, + reana_link_render, swh_link_render) APP_RDM_RECORD_LANDING_PAGE_EXTERNAL_LINKS = [ {"id": "f1000", "render": f1000_link_render}, @@ -925,6 +897,22 @@ OAISERVER_XSL_URL = "/static/oai2.xsl" RDM_DATACITE_FUNDER_IDENTIFIERS_PRIORITY = ("doi", "ror", "grid", "isni", "gnd") """Priority of funder identifiers types for Datacite serialization, prioritize DOI.""" +RDM_RECORDS_PERSONORG_SCHEMES = { +**RDM_RECORDS_PERSONORG_SCHEMES, + "edmo": {"label": _("EDMO"), "validator": is_edmo, "datacite": "URL"}, +} +"""Person and Organization Schemes.""" + +APP_RDM_IDENTIFIER_SCHEMES_UI = { + **APP_RDM_IDENTIFIER_SCHEMES_UI, + "edmo": { + "url_prefix": "https://edmo.seadatanet.org/report/", + "icon": "images/edmo-icon.svg", + "label": "EDMO", + } +} +"""Identifier Schemes UI config.""" + RECORDS_RESOURCES_ALLOW_EMPTY_FILES = False """Disable empty files for Zenodo.""" diff --git a/site/setup.cfg b/site/setup.cfg index 7bd0290c..58bfa192 100644 --- a/site/setup.cfg +++ b/site/setup.cfg @@ -88,6 +88,8 @@ invenio_previewer.previewers = image_previewer = zenodo_rdm.previewer.image_previewer invenio_search.index_templates = zenodo_rdm = zenodo_rdm.index_templates +idutils.custom_schemes = + edmo = zenodo_rdm.custom_schemes:get_scheme_config_func [bdist_wheel] universal = 1 diff --git a/site/zenodo_rdm/custom_schemes.py b/site/zenodo_rdm/custom_schemes.py new file mode 100644 index 00000000..ce643f35 --- /dev/null +++ b/site/zenodo_rdm/custom_schemes.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2024 CERN. +# +# ZenodoRDM is free software; you can redistribute it and/or modify it +# under the terms of the MIT License; see LICENSE file for more details. + +"""Custom scheme config.""" + +import re + +# Regular expression to validate EDMO identifiers in the format "edmo:" +edmo_regexp = re.compile(r"^edmo:(\d+)$") + +# Regular expression to match the EDMO URL format +edmo_url_regexp = re.compile( + r"(?:https?://)?(?:edmo\.seadatanet\.org/report/)?(\d+)$", re.IGNORECASE +) + + +def is_edmo(val): + """ + Validate and normalize an EDMO identifier or URL. + + Returns the normalized 'edmo:' format if valid, otherwise None. + """ + # Match against the URL or the edmo: format + match = edmo_url_regexp.match(val) or edmo_regexp.match(val) + if match: + # Extract the numeric ID from the matched group + return f"edmo:{match.group(1)}" + return None + + +def normalize_edmo(val): + """Normalize an EDMO identifier or URL to the 'edmo:' format.""" + return is_edmo(val) + + +def generate_edmo_url(_, normalized_pid): + """ + Generate a URL for a given normalized EDMO identifier. + + Assumes the input is in the form 'edmo:'. + """ + edmo_id = normalized_pid.split(":")[1] + return f"https://edmo.seadatanet.org/report/{edmo_id}" + + +def get_scheme_config_func(): + """Return the configuration for the EDMO custom scheme.""" + return { + "validator": is_edmo, + "normalizer": normalize_edmo, + "filter": [], + "url_generator": generate_edmo_url, + } diff --git a/static/images/edmo-icon.svg b/static/images/edmo-icon.svg new file mode 100644 index 00000000..a602f24a --- /dev/null +++ b/static/images/edmo-icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/templates/semantic-ui/zenodo_rdm/macros/creators.html b/templates/semantic-ui/zenodo_rdm/macros/creators.html index 24f94a26..caf3d1b1 100644 --- a/templates/semantic-ui/zenodo_rdm/macros/creators.html +++ b/templates/semantic-ui/zenodo_rdm/macros/creators.html @@ -53,6 +53,11 @@ {% set link = "https://ror.org/{identifier}".format(identifier=id.identifier) %} {% set link_title = _("ROR profile") %} {% set icon = "/static/images/ror-icon.svg" %} + + {% elif id.scheme == "edmo" %} + {% set link = "https://edmo.seadatanet.org/report/{identifier}".format(identifier=id.identifier) %} + {% set link_title = _("EDMO profile") %} + {% set icon = "/static/images/edmo-icon.svg" %} {% elif id.scheme == "gnd" %} {% set link = "https://d-nb.info/gnd/{identifier}".format(identifier=id.identifier) %} From 159a20fd8884b08050d46284606c7dde8e8b3c19 Mon Sep 17 00:00:00 2001 From: Fatimah Zulfiqar Date: Mon, 25 Nov 2024 15:41:44 +0100 Subject: [PATCH 2/2] js: added CustomAffiliationsSuggestion component --- .../CustomAffiliationsSuggestions.js | 59 +++++++++++++++++++ .../overridableRegistry/mapping.js | 2 + 2 files changed, 61 insertions(+) create mode 100644 assets/js/components/react_invenio_forms/CustomAffiliationsSuggestions.js diff --git a/assets/js/components/react_invenio_forms/CustomAffiliationsSuggestions.js b/assets/js/components/react_invenio_forms/CustomAffiliationsSuggestions.js new file mode 100644 index 00000000..eb92cfe9 --- /dev/null +++ b/assets/js/components/react_invenio_forms/CustomAffiliationsSuggestions.js @@ -0,0 +1,59 @@ +import React from "react"; +import { Header, Grid } from "semantic-ui-react"; +import { makeIdEntry, makeSubheader } from "react-invenio-forms"; + + +export const CustomAffiliationsSuggestions = (creatibutors) => { + // Ensure creatibutors is always an array + const creatibutorsArray = Array.isArray(creatibutors) ? creatibutors : [creatibutors]; + + return creatibutorsArray.map((creatibutor) => { + const { creatibutor: creatibutorData } = creatibutor; + const { name, acronym, identifiers = [], id } = creatibutorData; + + const subheader = makeSubheader(creatibutorData, creatibutor.isOrganization); + const displayName = acronym ? `${name} (${acronym})` : name; + + const sources = []; + const idStrings = []; + + identifiers.forEach((identifier) => { + const { scheme, identifier: value } = identifier; + + if (scheme === "ror") { + sources.push( + + Source: {scheme.toUpperCase()} (Preferred) + + ); + } else if (scheme === "edmo") { + sources.push( + Source: {scheme.toUpperCase()} + ); + } else { + const idEntry = makeIdEntry(identifier); + if (idEntry) idStrings.push(idEntry); + } + }); + + return ( + + + +
+ {displayName} {idStrings.length > 0 && <>({idStrings})} + {subheader && {subheader}} +
+
+ + {sources.length > 0 && ( +
+ {sources} +
+ )} +
+
+
+ ); + }); +}; diff --git a/assets/js/invenio_app_rdm/overridableRegistry/mapping.js b/assets/js/invenio_app_rdm/overridableRegistry/mapping.js index 594c4f22..dc541dda 100644 --- a/assets/js/invenio_app_rdm/overridableRegistry/mapping.js +++ b/assets/js/invenio_app_rdm/overridableRegistry/mapping.js @@ -7,10 +7,12 @@ import { UpgradeLegacyRecordButton } from "../../components/landing_page/overrides/UpgradeLegacyRecordButton"; import { FileUploaderNewVersion } from "../../components/landing_page/overrides/FileUploaderNewVersion"; import SubcommunityCreateForm from "../../components/community/overrides/SubcommunityCreateForm"; +import { CustomAffiliationsSuggestions } from "../../components/react_invenio_forms/CustomAffiliationsSuggestions"; export const overriddenComponents = { "InvenioAppRdm.RecordLandingPage.RecordManagement.container": UpgradeLegacyRecordButton, "ReactInvenioDeposit.FileUploader.NewVersionButton.container": FileUploaderNewVersion, + "ReactInvenioForms.AffiliationsSuggestions.content": CustomAffiliationsSuggestions, "InvenioCommunities.CommunityCreateForm.layout": SubcommunityCreateForm };